]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Merge St.TextureCache and Shell.TextureCache
authorColin Walters <walters@verbum.org>
Tue, 9 Feb 2010 17:42:07 +0000 (12:42 -0500)
committerColin Walters <walters@verbum.org>
Fri, 5 Mar 2010 21:19:40 +0000 (16:19 -0500)
Brute force merge these two by essentially replacing St.TextureCache
with a (renamed) Shell.TextureCache.

One function was added for convenience, namely "st_texture_cache_load_file_simple".
St.TextureCache had a function to load a texture from a filename, and it
returned NULL on error but only half the callers actually checked this.  This
function is better.

https://bugzilla.gnome.org/show_bug.cgi?id=607500

20 files changed:
configure.ac
js/misc/docInfo.js
js/ui/appDisplay.js
js/ui/dash.js
js/ui/docDisplay.js
js/ui/lookingGlass.js
js/ui/notificationDaemon.js
js/ui/placeDisplay.js
js/ui/runDialog.js
js/ui/statusMenu.js
src/Makefile.am
src/shell-app-system.c
src/shell-doc-system.c
src/shell-texture-cache.c [deleted file]
src/shell-texture-cache.h [deleted file]
src/shell-window-tracker.c
src/st/st-entry.c
src/st/st-texture-cache.c
src/st/st-texture-cache.h
src/st/st-widget.c

index 7d9de857d29727acaf077079bb0234179fe69d07..315bdfd89511d1f68b2580bfe38eba2b3169e23d 100644 (file)
@@ -59,10 +59,10 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
 PKG_CHECK_MODULES(MUTTER_PLUGIN, gio-unix-2.0 gtk+-2.0 dbus-glib-1 mutter-plugins >= 2.29.0
                                  gjs-gi-1.0 libgnome-menu $recorder_modules gconf-2.0
                                  gdk-x11-2.0 clutter-x11-1.0 clutter-glx-1.0
-                                 gnome-desktop-2.0 >= 2.26 libstartup-notification-1.0
+                                 libstartup-notification-1.0
                                  gobject-introspection-1.0 >= 0.6.5)
 PKG_CHECK_MODULES(TIDY, clutter-1.0)
-PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libcroco-0.6)
+PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-2.0 libcroco-0.6 gnome-desktop-2.0 >= 2.26)
 PKG_CHECK_MODULES(BIG, clutter-1.0 gtk+-2.0 librsvg-2.0)
 PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-2.0)
 PKG_CHECK_MODULES(TRAY, gtk+-2.0)
index 559e9ecef75132e9b91bacde79f31497b14e1e48..205e619d78bb25040b9143c00126f7f1d3c9d886 100644 (file)
@@ -3,8 +3,9 @@
 const Clutter = imports.gi.Clutter;
 const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
-const Shell = imports.gi.Shell;
 
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
 const Lang = imports.lang;
 const Signals = imports.signals;
 const Search = imports.ui.search;
@@ -30,7 +31,7 @@ DocInfo.prototype = {
     },
 
     createIcon : function(size) {
-        return Shell.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
+        return St.TextureCache.get_default().load_recent_thumbnail(size, this.recentInfo);
     },
 
     launch : function() {
index fdb2387ae916a4bfc76403a10e29215d6f7301fb..f18745f6c18ddfb2737cb53700ac5c7435e4973c 100644 (file)
@@ -694,7 +694,7 @@ AppIconMenu.prototype = {
         this._windowContainer.show();
 
         let iconsDiffer = false;
-        let texCache = Shell.TextureCache.get_default();
+        let texCache = St.TextureCache.get_default();
         if (windows.length > 0) {
             let firstIcon = windows[0].mini_icon;
             for (let i = 1; i < windows.length; i++) {
index 4082bcef7e06740a89d05740cb86279fd1f2a14d..041c2a11c738cff25eb5fabf33636c732e0c08c1 100644 (file)
@@ -268,11 +268,11 @@ SearchEntry.prototype = {
         box.append(this._iconBox, Big.BoxPackFlags.END);
 
         let magnifierUri = "file://" + global.imagedir + "magnifier.svg";
-        this._magnifierIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
-                                                                             magnifierUri, 18, 18);
+        this._magnifierIcon = St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.FOREVER,
+                                                                          magnifierUri, 18, 18);
         let closeUri = "file://" + global.imagedir + "close-black.svg";
-        this._closeIcon = Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.FOREVER,
-                                                                         closeUri, 18, 18);
+        this._closeIcon = St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.FOREVER,
+                                                                      closeUri, 18, 18);
         this._closeIcon.reactive = true;
         this._closeIcon.connect('button-press-event', Lang.bind(this, function () {
             // Resetting this.entry.text will trigger notify::text signal which will
index 1e0e97abe0dac2abfc823894f020cbd7b8dcc59b..b2d97fc94317245c7465889f663c93500ce52d9e 100644 (file)
@@ -85,8 +85,8 @@ DocDisplayItem.prototype = {
             return null;
 
         try {
-            return Shell.TextureCache.get_default().load_uri_sync(Shell.TextureCachePolicy.NONE,
-                                                                  this._docInfo.uri, -1, -1);
+            return St.TextureCache.get_default().load_uri_sync(St.TextureCachePolicy.NONE,
+                                                               this._docInfo.uri, -1, -1);
         } catch (e) {
             // An exception will be raised when the image format isn't know
             /* FIXME: http://bugzilla.gnome.org/show_bug.cgi?id=591480: should
index 403324b5fa35a4d93fe27fe022a870bf241e6511..08d191bfdafa2a9603491e23b9594843413bef08 100644 (file)
@@ -484,8 +484,8 @@ LookingGlass.prototype = {
 
         let toolbar = new St.BoxLayout({ name: "Toolbar" });
         this.actor.add_actor(toolbar);
-        let inspectIcon = Shell.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
-                                                                      24);
+        let inspectIcon = St.TextureCache.get_default().load_gicon(new Gio.ThemedIcon({ name: 'gtk-color-picker' }),
+                                                                   24);
         toolbar.add_actor(inspectIcon);
         inspectIcon.reactive = true;
         inspectIcon.connect('button-press-event', Lang.bind(this, function () {
index 2977adcb9e48799ccef69e1e90699a20355023ff..0183962afe6d64ecb31acf89952ea25ae3b0dbb1 100644 (file)
@@ -318,7 +318,7 @@ Source.prototype = {
     },
 
     createIcon: function(size) {
-        let textureCache = Shell.TextureCache.get_default();
+        let textureCache = St.TextureCache.get_default();
 
         if (this._icon) {
             if (this._icon.substr(0, 7) == 'file://')
index 666ba606796d53c58faa0a2eca0706b201629f6e..da7fe035667b1c7a4f9e4f622c7af10ddd81016b 100644 (file)
@@ -77,7 +77,7 @@ PlaceDeviceInfo.prototype = {
 
     iconFactory: function(size) {
         let icon = this._mount.get_icon();
-        return Shell.TextureCache.get_default().load_gicon(icon, size);
+        return St.TextureCache.get_default().load_gicon(icon, size);
     },
 
     launch: function() {
@@ -122,7 +122,7 @@ PlacesManager.prototype = {
         let homeIcon = Shell.util_get_icon_for_uri (homeUri);
         this._home = new PlaceInfo('special:home', homeLabel,
             function(size) {
-                return Shell.TextureCache.get_default().load_gicon(homeIcon, size);
+                return St.TextureCache.get_default().load_gicon(homeIcon, size);
             },
             function() {
                 Gio.app_info_launch_default_for_uri(homeUri, global.create_app_launch_context());
@@ -135,7 +135,7 @@ PlacesManager.prototype = {
         let desktopIcon = Shell.util_get_icon_for_uri (desktopUri);
         this._desktopMenu = new PlaceInfo('special:desktop', desktopLabel,
             function(size) {
-                return Shell.TextureCache.get_default().load_gicon(desktopIcon, size);
+                return St.TextureCache.get_default().load_gicon(desktopIcon, size);
             },
             function() {
                 Gio.app_info_launch_default_for_uri(desktopUri, global.create_app_launch_context());
@@ -143,7 +143,7 @@ PlacesManager.prototype = {
 
         this._connect = new PlaceInfo('special:connect', _("Connect to..."),
             function (size) {
-                return Shell.TextureCache.get_default().load_icon_name("applications-internet", size);
+                return St.TextureCache.get_default().load_icon_name("applications-internet", size);
             },
             function () {
                 new Shell.Process({ args: ['nautilus-connect-server'] }).run();
@@ -310,7 +310,7 @@ PlacesManager.prototype = {
 
             let item = new PlaceInfo('bookmark:' + bookmark, label,
                 function(size) {
-                    return Shell.TextureCache.get_default().load_gicon(icon, size);
+                    return St.TextureCache.get_default().load_gicon(icon, size);
                 },
                 function() {
                     Gio.app_info_launch_default_for_uri(bookmark, global.create_app_launch_context());
@@ -414,7 +414,7 @@ DashPlaceDisplayItem.prototype = {
         this.actor.append(text, Big.BoxPackFlags.EXPAND);
 
         if (info.isRemovable()) {
-            let removeIcon = Shell.TextureCache.get_default().load_icon_name ('media-eject', PLACES_ICON_SIZE);
+            let removeIcon = St.TextureCache.get_default().load_icon_name ('media-eject', PLACES_ICON_SIZE);
             let removeIconBox = new St.Button({ child: removeIcon,
                                                 reactive: true });
             this.actor.append(removeIconBox, Big.BoxPackFlags.NONE);
index 160542bc1be693ef4b1cf6f6e493c4ecc48a95f7..11008a7fecc4560695a338c45d5d2b68d8ccc356 100644 (file)
@@ -7,9 +7,9 @@ const GLib = imports.gi.GLib;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
 const Meta = imports.gi.Meta;
+const St = imports.gi.St;
 const Shell = imports.gi.Shell;
 const Signals = imports.signals;
-const St = imports.gi.St;
 const Gettext = imports.gettext.domain('gnome-shell');
 const _ = Gettext.gettext;
 
index e5bf081340534c608df2e63d3630ce91bd094a0d..e8e66d9da4049efc42e9821665946498f959ae37 100644 (file)
@@ -36,7 +36,7 @@ StatusMenu.prototype = {
         this._iconBox = new St.Bin();
         this.actor.add(this._iconBox, { y_align: St.Align.MIDDLE });
 
-        let textureCache = Shell.TextureCache.get_default();
+        let textureCache = St.TextureCache.get_default();
         // FIXME: these icons are all wrong (likewise in createSubMenu)
         this._availableIcon = textureCache.load_icon_name('gtk-yes', 16);
         this._busyIcon = textureCache.load_icon_name('gtk-no', 16);
index 99d06367d7267fdd942d9d53795ed2144d297c09..74d58c15f133e382c2c30d16cbc9787ddd20c944 100644 (file)
@@ -90,8 +90,6 @@ libgnome_shell_la_SOURCES =                   \
        shell-stack.h                           \
        shell-tray-manager.c                    \
        shell-tray-manager.h                    \
-       shell-texture-cache.c                   \
-       shell-texture-cache.h                   \
        shell-uri-util.c           \
        shell-uri-util.h           \
        shell-window-tracker.c          \
index 4d8b7c809c496e321efc9800f974794005bf4b5d..be07df7d3e6b82cc5ab5c85de0b59dcd9c8be5ed 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "shell-app-private.h"
 #include "shell-global.h"
-#include "shell-texture-cache.h"
+#include "st/st-texture-cache.h"
 #include "display.h"
 
 #define GMENU_I_KNOW_THIS_IS_UNSTABLE
@@ -1100,9 +1100,9 @@ shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
 
   if (info->type == SHELL_APP_INFO_TYPE_WINDOW)
     {
-      return shell_texture_cache_bind_pixbuf_property (shell_texture_cache_get_default (),
-                                                       G_OBJECT (info->window),
-                                                       "icon");
+      return st_texture_cache_bind_pixbuf_property (st_texture_cache_get_default (),
+                                                    G_OBJECT (info->window),
+                                                    "icon");
     }
 
   icon = shell_app_info_get_icon (info);
@@ -1113,7 +1113,7 @@ shell_app_info_create_icon_texture (ShellAppInfo *info, float size)
     }
   else
     {
-      ret = shell_texture_cache_load_gicon (shell_texture_cache_get_default (), icon, (int)size);
+      ret = st_texture_cache_load_gicon (st_texture_cache_get_default (), icon, (int)size);
       g_object_unref (icon);
     }
 
index ecb99736ec770646d845966111830222a7bb19e6..ee2eb99c26ba7082720a42c607380b7fcb1aee05 100644 (file)
@@ -5,7 +5,6 @@
 #include "shell-doc-system.h"
 
 #include "shell-global.h"
-#include "shell-texture-cache.h"
 
 
 /**
diff --git a/src/shell-texture-cache.c b/src/shell-texture-cache.c
deleted file mode 100644 (file)
index 9b022ba..0000000
+++ /dev/null
@@ -1,1499 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-
-#include "config.h"
-
-#include "shell-texture-cache.h"
-#include "shell-global.h"
-#include <gtk/gtk.h>
-#define GNOME_DESKTOP_USE_UNSTABLE_API
-#include <libgnomeui/gnome-desktop-thumbnail.h>
-#include <string.h>
-#include <glib.h>
-
-typedef struct
-{
-  ShellTextureCachePolicy policy;
-
-  /* These are exclusive */
-  GIcon *icon;
-  gchar *uri;
-  gchar *thumbnail_uri;
-  gchar *checksum;
-
-  /* This one is common to all */
-  guint size;
-} CacheKey;
-
-struct _ShellTextureCachePrivate
-{
-  /* Things that were loaded with a cache policy != NONE */
-  GHashTable *keyed_cache; /* CacheKey -> CoglTexture* */
-  /* Presently this is used to de-duplicate requests for GIcons,
-   * it could in theory be extended to async URL loading and other
-   * cases too.
-   */
-  GHashTable *outstanding_requests; /* CacheKey -> AsyncTextureLoadData * */
-  GnomeDesktopThumbnailFactory *thumbnails;
-};
-
-static void shell_texture_cache_dispose (GObject *object);
-static void shell_texture_cache_finalize (GObject *object);
-
-G_DEFINE_TYPE(ShellTextureCache, shell_texture_cache, G_TYPE_OBJECT);
-
-static guint
-cache_key_hash (gconstpointer a)
-{
-  CacheKey *akey = (CacheKey *)a;
-  guint base_hash;
-
-  if (akey->icon)
-    base_hash = g_icon_hash (akey->icon);
-  else if (akey->uri)
-    base_hash = g_str_hash (akey->uri);
-  else if (akey->thumbnail_uri)
-    base_hash = g_str_hash (akey->thumbnail_uri);
-  else if (akey->checksum)
-    base_hash = g_str_hash (akey->checksum);
-  else
-    g_assert_not_reached ();
-  return base_hash + 31*akey->size;
-}
-
-static gboolean
-cache_key_equal (gconstpointer a,
-                 gconstpointer b)
-{
-  CacheKey *akey = (CacheKey*)a;
-  CacheKey *bkey = (CacheKey*)b;
-
-  /* We don't compare policy here, since we need
-   * a way to look up a cache key without respect to
-   * the policy. */
-
-  if (akey->size != bkey->size)
-    return FALSE;
-
-  if (akey->icon && bkey->icon)
-    return g_icon_equal (akey->icon, bkey->icon);
-  else if (akey->uri && bkey->uri)
-    return strcmp (akey->uri, bkey->uri) == 0;
-  else if (akey->thumbnail_uri && bkey->thumbnail_uri)
-    return strcmp (akey->thumbnail_uri, bkey->thumbnail_uri) == 0;
-  else if (akey->checksum && bkey->checksum)
-    return strcmp (akey->checksum, bkey->checksum) == 0;
-
-  return FALSE;
-}
-
-static CacheKey *
-cache_key_dup (CacheKey *key)
-{
-  CacheKey *ret = g_new0 (CacheKey, 1);
-  ret->policy = key->policy;
-  if (key->icon)
-    ret->icon = g_object_ref (key->icon);
-  ret->uri = g_strdup (key->uri);
-  ret->thumbnail_uri = g_strdup (key->thumbnail_uri);
-  ret->checksum = g_strdup (key->checksum);
-  ret->size = key->size;
-  return ret;
-}
-
-static void
-cache_key_destroy (gpointer a)
-{
-  CacheKey *akey = (CacheKey*)a;
-  if (akey->icon)
-    g_object_unref (akey->icon);
-  g_free (akey->uri);
-  g_free (akey->thumbnail_uri);
-  g_free (akey->checksum);
-  g_free (akey);
-}
-
-
-/* We want to preserve the aspect ratio by default, also the default
- * material for an empty texture is full opacity white, which we
- * definitely don't want.  Skip that by setting 0 opacity.
- */
-static ClutterTexture *
-create_default_texture (ShellTextureCache *self)
-{
-  ClutterTexture * texture = CLUTTER_TEXTURE (clutter_texture_new ());
-  g_object_set (texture, "keep-aspect-ratio", TRUE, "opacity", 0, NULL);
-  return texture;
-}
-
-/* Reverse the opacity we added while loading */
-static void
-set_texture_cogl_texture (ClutterTexture *clutter_texture, CoglHandle cogl_texture)
-{
-  clutter_texture_set_cogl_texture (clutter_texture, cogl_texture);
-  g_object_set (clutter_texture, "opacity", 255, NULL);
-}
-
-static void
-shell_texture_cache_class_init (ShellTextureCacheClass *klass)
-{
-  GObjectClass *gobject_class = (GObjectClass *)klass;
-
-  gobject_class->dispose = shell_texture_cache_dispose;
-  gobject_class->finalize = shell_texture_cache_finalize;
-}
-
-static void
-shell_texture_cache_init (ShellTextureCache *self)
-{
-  self->priv = g_new0 (ShellTextureCachePrivate, 1);
-  self->priv->keyed_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
-                                                   cache_key_destroy, cogl_handle_unref);
-  self->priv->outstanding_requests = g_hash_table_new_full (cache_key_hash, cache_key_equal,
-                                                            cache_key_destroy, NULL);
-  self->priv->thumbnails = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
-}
-
-static void
-shell_texture_cache_dispose (GObject *object)
-{
-  ShellTextureCache *self = (ShellTextureCache*)object;
-
-  if (self->priv->keyed_cache)
-    g_hash_table_destroy (self->priv->keyed_cache);
-  self->priv->keyed_cache = NULL;
-
-  if (self->priv->thumbnails)
-    g_object_unref (self->priv->thumbnails);
-  self->priv->thumbnails = NULL;
-
-  G_OBJECT_CLASS (shell_texture_cache_parent_class)->dispose (object);
-}
-
-static void
-shell_texture_cache_finalize (GObject *object)
-{
-  G_OBJECT_CLASS (shell_texture_cache_parent_class)->finalize (object);
-}
-
-typedef struct {
-  ShellTextureCache *cache;
-  char *uri;
-  char *mimetype;
-  gboolean thumbnail;
-  GIcon *icon;
-  GtkRecentInfo *recent_info;
-  GtkIconInfo *icon_info;
-  gint width;
-  gint height;
-  gpointer user_data;
-} AsyncIconLookupData;
-
-static gboolean
-compute_pixbuf_scale (gint      width,
-                      gint      height,
-                      gint      available_width,
-                      gint      available_height,
-                      gint     *new_width,
-                      gint     *new_height)
-{
-  int scaled_width, scaled_height;
-
-  if (width == 0 || height == 0)
-    return FALSE;
-
-  if (available_width >= 0 && available_height >= 0)
-    {
-      // This should keep the aspect ratio of the image intact, because if
-      // available_width < (available_height * width) / height
-      // than
-      // (available_width * height) / width < available_height
-      // So we are guaranteed to either scale the image to have an available_width
-      // for width and height scaled accordingly OR have the available_height
-      // for height and width scaled accordingly, whichever scaling results
-      // in the image that can fit both available dimensions.
-      scaled_width = MIN (available_width, (available_height * width) / height);
-      scaled_height = MIN (available_height, (available_width * height) / width);
-    }
-  else if (available_width >= 0)
-    {
-      scaled_width = available_width;
-      scaled_height = (available_width * height) / width;
-    }
-  else if (available_height >= 0)
-    {
-      scaled_width = (available_height * width) / height;
-      scaled_height = available_height;
-    }
-  else
-    {
-      scaled_width = scaled_height = 0;
-    }
-
-  // Scale the image only if that will not increase its original dimensions.
-  if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height)
-    {
-      *new_width = scaled_width;
-      *new_height = scaled_height;
-      return TRUE;
-    }
-  return FALSE;
-}
-
-static GdkPixbuf *
-impl_load_pixbuf_gicon (GIcon       *icon,
-                        GtkIconInfo *info,
-                        int          size,
-                        GError     **error)
-{
-  int scaled_width, scaled_height;
-  GdkPixbuf *pixbuf = gtk_icon_info_load_icon (info, error);
-  int width, height;
-
-  if (!pixbuf)
-    return NULL;
-
-  width = gdk_pixbuf_get_width (pixbuf);
-  height = gdk_pixbuf_get_height (pixbuf);
-
-  if (compute_pixbuf_scale (width,
-                            height,
-                            size, size,
-                            &scaled_width, &scaled_height))
-    {
-      GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
-      g_object_unref (pixbuf);
-      pixbuf = scaled;
-    }
-  return pixbuf;
-}
-
-// A private structure for keeping width and height.
-typedef struct {
-  int width;
-  int height;
-} Dimensions;
-
-static void
-icon_lookup_data_destroy (gpointer p)
-{
-  AsyncIconLookupData *data = p;
-
-  if (data->icon)
-    {
-      g_object_unref (data->icon);
-      gtk_icon_info_free (data->icon_info);
-    }
-  else if (data->uri)
-    g_free (data->uri);
-  if (data->mimetype)
-    g_free (data->mimetype);
-  if (data->recent_info)
-    gtk_recent_info_unref (data->recent_info);
-
-  g_free (data);
-}
-
-/**
- * on_image_size_prepared:
- *
- * @pixbuf_loader: #GdkPixbufLoader loading the image
- * @width: the original width of the image
- * @height: the original height of the image
- * @data: pointer to the #Dimensions sructure containing available width and height for the image,
- *        available width or height can be -1 if the dimension is not limited
- *
- * Private function.
- *
- * Sets the size of the image being loaded to fit the available width and height dimensions,
- * but never scales up the image beyond its actual size.
- * Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal.
- */
-static void
-on_image_size_prepared (GdkPixbufLoader *pixbuf_loader,
-                        gint             width,
-                        gint             height,
-                        gpointer         data)
-{
-  Dimensions *available_dimensions = data;
-  int available_width = available_dimensions->width;
-  int available_height = available_dimensions->height;
-  int scaled_width;
-  int scaled_height;
-
-  if (compute_pixbuf_scale (width, height, available_width, available_height,
-                            &scaled_width, &scaled_height))
-    gdk_pixbuf_loader_set_size (pixbuf_loader, scaled_width, scaled_height);
-}
-
-static GdkPixbuf *
-impl_load_pixbuf_data (const guchar   *data,
-                       gsize           size,
-                       int             available_width,
-                       int             available_height,
-                       GError        **error)
-{
-  GdkPixbufLoader *pixbuf_loader = NULL;
-  GdkPixbuf *rotated_pixbuf = NULL;
-  GdkPixbuf *pixbuf;
-  gboolean success;
-  Dimensions available_dimensions;
-  int width_before_rotation, width_after_rotation;
-
-  pixbuf_loader = gdk_pixbuf_loader_new ();
-
-  available_dimensions.width = available_width;
-  available_dimensions.height = available_height;
-  g_signal_connect (pixbuf_loader, "size-prepared",
-                    G_CALLBACK (on_image_size_prepared), &available_dimensions);
-
-  success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
-  if (!success)
-    goto out;
-  success = gdk_pixbuf_loader_close (pixbuf_loader, error);
-  if (!success)
-    goto out;
-
-  pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
-
-  width_before_rotation = gdk_pixbuf_get_width (pixbuf);
-
-  rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
-  width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
-
-  // There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
-  // so we only check that once it is loaded, and reload it again if it needs to be rotated in order
-  // to use the available width and height correctly.
-  // http://bugzilla.gnome.org/show_bug.cgi?id=579003
-  if (width_before_rotation != width_after_rotation)
-    {
-      g_object_unref (pixbuf_loader);
-      g_object_unref (rotated_pixbuf);
-      rotated_pixbuf = NULL;
-
-      pixbuf_loader = gdk_pixbuf_loader_new ();
-
-      // We know that the image will later be rotated, so we reverse the available dimensions.
-      available_dimensions.width = available_height;
-      available_dimensions.height = available_width;
-      g_signal_connect (pixbuf_loader, "size-prepared",
-                        G_CALLBACK (on_image_size_prepared), &available_dimensions);
-
-      success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
-      if (!success)
-        goto out;
-
-      success = gdk_pixbuf_loader_close (pixbuf_loader, error);
-      if (!success)
-        goto out;
-
-      pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
-
-      rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
-    }
-
-out:
-  if (pixbuf_loader)
-    g_object_unref (pixbuf_loader);
-  return rotated_pixbuf;
-}
-
-static GdkPixbuf *
-impl_load_pixbuf_file (const char     *uri,
-                       int             available_width,
-                       int             available_height,
-                       GError        **error)
-{
-  GdkPixbuf *pixbuf = NULL;
-  GFile *file;
-  char *contents = NULL;
-  gsize size;
-
-  file = g_file_new_for_uri (uri);
-  if (g_file_load_contents (file, NULL, &contents, &size, NULL, error))
-    {
-      pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size,
-                                      available_width, available_height,
-                                      error);
-    }
-
-  g_object_unref (file);
-  g_free (contents);
-
-  return pixbuf;
-}
-
-static GdkPixbuf *
-impl_load_thumbnail (ShellTextureCache *cache,
-                     const char        *uri,
-                     const char        *mime_type,
-                     guint              size,
-                     GError           **error)
-{
-  GnomeDesktopThumbnailFactory *thumbnail_factory;
-  GdkPixbuf *pixbuf = NULL;
-  GFile *file;
-  GFileInfo *file_info;
-  GTimeVal mtime_g;
-  time_t mtime = 0;
-  char *existing_thumbnail;
-
-  file = g_file_new_for_uri (uri);
-  file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
-  g_object_unref (file);
-  if (file_info)
-    {
-      g_file_info_get_modification_time (file_info, &mtime_g);
-      g_object_unref (file_info);
-      mtime = (time_t) mtime_g.tv_sec;
-    }
-
-  thumbnail_factory = cache->priv->thumbnails;
-
-  existing_thumbnail = gnome_desktop_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
-
-  if (existing_thumbnail != NULL)
-    {
-      pixbuf = gdk_pixbuf_new_from_file_at_size (existing_thumbnail, size, size, error);
-      g_free (existing_thumbnail);
-    }
-  else if (gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
-    g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Has failed thumbnail");
-  else if (gnome_desktop_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
-    {
-      pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
-      if (pixbuf)
-        {
-          // we need to save the thumbnail so that we don't need to generate it again in the future
-          gnome_desktop_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
-        }
-      else
-        {
-          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to generate thumbnail");
-          gnome_desktop_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
-        }
-     }
-   return pixbuf;
-}
-
-static GIcon *
-icon_for_mimetype (const char *mimetype)
-{
-  char *content_type;
-  GIcon *icon;
-
-  content_type = g_content_type_from_mime_type (mimetype);
-  if (!content_type)
-    return NULL;
-
-  icon = g_content_type_get_icon (content_type);
-  g_free (content_type);
-  return icon;
-}
-
-static void
-load_pixbuf_thread (GSimpleAsyncResult *result,
-                    GObject *object,
-                    GCancellable *cancellable)
-{
-  GdkPixbuf *pixbuf;
-  AsyncIconLookupData *data;
-  GError *error = NULL;
-
-  data = g_object_get_data (G_OBJECT (result), "load_pixbuf_async");
-  g_assert (data != NULL);
-
-  if (data->thumbnail)
-    {
-      const char *uri;
-      const char *mimetype;
-
-      if (data->recent_info)
-        {
-          uri = gtk_recent_info_get_uri (data->recent_info);
-          mimetype = gtk_recent_info_get_mime_type (data->recent_info);
-        }
-      else
-        {
-          uri = data->uri;
-          mimetype = data->mimetype;
-        }
-      pixbuf = impl_load_thumbnail (data->cache, uri, mimetype, data->width, &error);
-    }
-  else if (data->uri)
-    pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
-  else if (data->icon)
-    pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, &error);
-  else
-    g_assert_not_reached ();
-
-  if (error != NULL)
-    {
-      g_simple_async_result_set_from_error (result, error);
-      return;
-    }
-
-  if (pixbuf)
-    g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
-                                               g_object_unref);
-}
-
-/**
- * load_icon_pixbuf_async:
- *
- * Asynchronously load the #GdkPixbuf associated with a #GIcon.  Currently
- * the #GtkIconInfo must have already been provided.
- */
-static void
-load_icon_pixbuf_async (ShellTextureCache    *cache,
-                        GIcon                *icon,
-                        GtkIconInfo          *icon_info,
-                        gint                  size,
-                        GCancellable         *cancellable,
-                        GAsyncReadyCallback   callback,
-                        gpointer              user_data)
-{
-  GSimpleAsyncResult *result;
-  AsyncIconLookupData *data;
-
-  data = g_new0 (AsyncIconLookupData, 1);
-  data->cache = cache;
-  data->icon = g_object_ref (icon);
-  data->icon_info = gtk_icon_info_copy (icon_info);
-  data->width = data->height = size;
-  data->user_data = user_data;
-
-  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
-
-  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
-  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
-
-  g_object_unref (result);
-}
-
-static void
-load_uri_pixbuf_async (ShellTextureCache *cache,
-                       const char *uri,
-                       guint width,
-                       guint height,
-                       GCancellable *cancellable,
-                       GAsyncReadyCallback callback,
-                       gpointer user_data)
-{
-  GSimpleAsyncResult *result;
-  AsyncIconLookupData *data;
-
-  data = g_new0 (AsyncIconLookupData, 1);
-  data->cache = cache;
-  data->uri = g_strdup (uri);
-  data->width = width;
-  data->height = height;
-  data->user_data = user_data;
-
-  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
-
-  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
-  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
-
-  g_object_unref (result);
-}
-
-static void
-load_thumbnail_async (ShellTextureCache  *cache,
-                      const char         *uri,
-                      const char         *mimetype,
-                      guint               size,
-                      GCancellable       *cancellable,
-                      GAsyncReadyCallback callback,
-                      gpointer            user_data)
-{
-  GSimpleAsyncResult *result;
-  AsyncIconLookupData *data;
-
-  data = g_new0 (AsyncIconLookupData, 1);
-  data->cache = cache;
-  data->uri = g_strdup (uri);
-  data->mimetype = g_strdup (mimetype);
-  data->thumbnail = TRUE;
-  data->width = size;
-  data->height = size;
-  data->user_data = user_data;
-
-  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_thumbnail_async);
-
-  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
-  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
-
-  g_object_unref (result);
-}
-
-static void
-load_recent_thumbnail_async (ShellTextureCache  *cache,
-                             GtkRecentInfo      *info,
-                             guint               size,
-                             GCancellable       *cancellable,
-                             GAsyncReadyCallback callback,
-                             gpointer            user_data)
-{
-  GSimpleAsyncResult *result;
-  AsyncIconLookupData *data;
-
-  data = g_new0 (AsyncIconLookupData, 1);
-  data->cache = cache;
-  data->thumbnail = TRUE;
-  data->recent_info = gtk_recent_info_ref (info);
-  data->width = size;
-  data->height = size;
-  data->user_data = user_data;
-
-  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_recent_thumbnail_async);
-
-  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
-  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
-
-  g_object_unref (result);
-}
-
-static GdkPixbuf *
-load_pixbuf_async_finish (ShellTextureCache *cache, GAsyncResult *result, GError **error)
-{
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-  return g_simple_async_result_get_op_res_gpointer (simple);
-}
-
-typedef struct {
-  ShellTextureCachePolicy policy;
-  char *uri;
-  gboolean thumbnail;
-  char *mimetype;
-  GtkRecentInfo *recent_info;
-  char *checksum;
-  GIcon *icon;
-  GtkIconInfo *icon_info;
-  guint width;
-  guint height;
-  GSList *textures;
-} AsyncTextureLoadData;
-
-static CoglHandle
-pixbuf_to_cogl_handle (GdkPixbuf *pixbuf)
-{
-  return cogl_texture_new_from_data (gdk_pixbuf_get_width (pixbuf),
-                                     gdk_pixbuf_get_height (pixbuf),
-                                     COGL_TEXTURE_NONE,
-                                     gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
-                                     COGL_PIXEL_FORMAT_ANY,
-                                     gdk_pixbuf_get_rowstride (pixbuf),
-                                     gdk_pixbuf_get_pixels (pixbuf));
-}
-
-static GdkPixbuf *
-load_pixbuf_fallback(AsyncTextureLoadData *data)
-{
-  GdkPixbuf *pixbuf = NULL;
-
-  if (data->thumbnail)
-    {
-
-      GtkIconTheme *theme = gtk_icon_theme_get_default ();
-
-      if (data->recent_info)
-          pixbuf = gtk_recent_info_get_icon (data->recent_info, data->width);
-      else
-        {
-          GIcon *icon = icon_for_mimetype (data->mimetype);
-          if (icon != NULL)
-            {
-              GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon (theme,
-                                                                       icon,
-                                                                       data->width,
-                                                                       GTK_ICON_LOOKUP_USE_BUILTIN);
-              g_object_unref (icon);
-              if (icon_info != NULL)
-                pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
-            }
-        }
-
-      if (pixbuf == NULL)
-        pixbuf = gtk_icon_theme_load_icon (theme,
-                                           "gtk-file",
-                                           data->width,
-                                           GTK_ICON_LOOKUP_USE_BUILTIN,
-                                           NULL);
-    }
-  /* Maybe we could need a fallback for outher image types? */
-
-  return pixbuf;
-}
-
-static void
-on_pixbuf_loaded (GObject      *source,
-                  GAsyncResult *result,
-                  gpointer      user_data)
-{
-  GSList *iter;
-  ShellTextureCache *cache;
-  AsyncTextureLoadData *data;
-  GdkPixbuf *pixbuf;
-  GError *error = NULL;
-  CoglHandle texdata = NULL;
-  CacheKey key;
-
-  data = user_data;
-  cache = SHELL_TEXTURE_CACHE (source);
-
-  memset (&key, 0, sizeof(key));
-  key.policy = data->policy;
-  if (data->icon)
-    key.icon = data->icon;
-  else if (data->recent_info && data->thumbnail)
-    key.thumbnail_uri = (char*)gtk_recent_info_get_uri (data->recent_info);
-  else if (data->thumbnail)
-    key.thumbnail_uri = (char*)data->uri;
-  else if (data->uri)
-    key.uri = data->uri;
-  key.size = data->width;
-
-  g_hash_table_remove (cache->priv->outstanding_requests, &key);
-
-  pixbuf = load_pixbuf_async_finish (cache, result, &error);
-  if (pixbuf == NULL)
-    pixbuf = load_pixbuf_fallback(data);
-  if (pixbuf == NULL)
-    goto out;
-
-  texdata = pixbuf_to_cogl_handle (pixbuf);
-
-  g_object_unref (pixbuf);
-
-  if (data->policy != SHELL_TEXTURE_CACHE_POLICY_NONE)
-    {
-      gpointer orig_key, value;
-
-      if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, &key,
-                                         &orig_key, &value))
-        {
-          cogl_handle_ref (texdata);
-          g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key),
-                               texdata);
-        }
-    }
-
-  for (iter = data->textures; iter; iter = iter->next)
-    {
-      ClutterTexture *texture = iter->data;
-      set_texture_cogl_texture (texture, texdata);
-    }
-
-out:
-  if (texdata)
-    cogl_handle_unref (texdata);
-  if (data->icon)
-    {
-      gtk_icon_info_free (data->icon_info);
-      g_object_unref (data->icon);
-    }
-  else if (data->uri)
-    g_free (data->uri);
-
-  if (data->recent_info)
-    gtk_recent_info_unref (data->recent_info);
-  if (data->mimetype)
-    g_free (data->mimetype);
-
-  /* Alternatively we could weakref and just do nothing if the texture
-     is destroyed */
-  for (iter = data->textures; iter; iter = iter->next)
-    {
-      ClutterTexture *texture = iter->data;
-      g_object_unref (texture);
-    }
-
-  g_clear_error (&error);
-  g_free (data);
-}
-
-typedef struct {
-  ShellTextureCache *cache;
-  ClutterTexture *texture;
-  GObject *source;
-  guint notify_signal_id;
-  gboolean weakref_active;
-} ShellTextureCachePropertyBind;
-
-static void
-shell_texture_cache_reset_texture (ShellTextureCachePropertyBind *bind, const char *propname)
-{
-  GdkPixbuf *pixbuf;
-  CoglHandle texdata;
-
-  g_object_get (bind->source, propname, &pixbuf, NULL);
-
-  g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
-
-  if (pixbuf != NULL)
-    {
-      texdata = pixbuf_to_cogl_handle (pixbuf);
-      g_object_unref (pixbuf);
-
-      clutter_texture_set_cogl_texture (bind->texture, texdata);
-      cogl_handle_unref (texdata);
-
-      clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 255);
-    }
-  else
-    clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 0);
-}
-
-static void
-shell_texture_cache_on_pixbuf_notify (GObject           *object,
-                                      GParamSpec        *paramspec,
-                                      gpointer           data)
-{
-  ShellTextureCachePropertyBind *bind = data;
-  shell_texture_cache_reset_texture (bind, paramspec->name);
-}
-
-static void
-shell_texture_cache_bind_weak_notify (gpointer     data,
-                                      GObject     *source_location)
-{
-  ShellTextureCachePropertyBind *bind = data;
-  bind->weakref_active = FALSE;
-  g_signal_handler_disconnect (bind->source, bind->notify_signal_id);
-}
-
-static void
-shell_texture_cache_free_bind (gpointer data)
-{
-  ShellTextureCachePropertyBind *bind = data;
-  if (bind->weakref_active)
-    g_object_weak_unref (G_OBJECT(bind->texture), shell_texture_cache_bind_weak_notify, bind);
-  g_free (bind);
-}
-
-/**
- * shell_texture_cache_bind_pixbuf_property:
- * @cache:
- * @object: A #GObject with a property @property_name of type #GdkPixbuf
- * @property_name: Name of a property
- *
- * Create a #ClutterTexture which tracks the #GdkPixbuf value of a GObject property
- * named by @property_name.  Unlike other methods in ShellTextureCache, the underlying
- * CoglHandle is not shared by default with other invocations to this method.
- *
- * If the source object is destroyed, the texture will continue to show the last
- * value of the property.
- *
- * Return value: (transfer none): A new #ClutterActor
- */
-ClutterActor *
-shell_texture_cache_bind_pixbuf_property (ShellTextureCache *cache,
-                                          GObject           *object,
-                                          const char        *property_name)
-{
-  ClutterTexture *texture;
-  gchar *notify_key;
-  ShellTextureCachePropertyBind *bind;
-
-  texture = CLUTTER_TEXTURE (clutter_texture_new ());
-
-  bind = g_new0 (ShellTextureCachePropertyBind, 1);
-  bind->cache = cache;
-  bind->texture = texture;
-  bind->source = object;
-  g_object_weak_ref (G_OBJECT (texture), shell_texture_cache_bind_weak_notify, bind);
-  bind->weakref_active = TRUE;
-
-  shell_texture_cache_reset_texture (bind, property_name);
-
-  notify_key = g_strdup_printf ("notify::%s", property_name);
-  bind->notify_signal_id = g_signal_connect_data (object, notify_key, G_CALLBACK(shell_texture_cache_on_pixbuf_notify),
-                                                  bind, (GClosureNotify)shell_texture_cache_free_bind, 0);
-  g_free (notify_key);
-
-  return CLUTTER_ACTOR(texture);
-}
-
-/**
- * create_texture_and_ensure_request:
- * @cache:
- * @key: A filled in #CacheKey
- * @request: (out): If no request is outstanding, one will be created and returned here
- * @texture: (out): A new texture, also added to the request
- *
- * Check for any outstanding load for the data represented by @key.  If there
- * is already a request pending, append it to that request to avoid loading
- * the data multiple times.
- *
- * Returns: %TRUE iff there is already a request pending
- */
-static gboolean
-create_texture_and_ensure_request (ShellTextureCache     *cache,
-                                   CacheKey              *key,
-                                   AsyncTextureLoadData **request,
-                                   ClutterActor         **texture)
-{
-  CoglHandle texdata;
-  AsyncTextureLoadData *pending;
-  gboolean had_pending;
-
-  *texture = (ClutterActor *) create_default_texture (cache);
-  clutter_actor_set_size (*texture, key->size, key->size);
-
-  texdata = g_hash_table_lookup (cache->priv->keyed_cache, key);
-
-  if (texdata != NULL)
-    {
-      /* We had this cached already, just set the texture and we're done. */
-      set_texture_cogl_texture (CLUTTER_TEXTURE (*texture), texdata);
-      return TRUE;
-    }
-
-  pending = g_hash_table_lookup (cache->priv->outstanding_requests, key);
-  had_pending = pending != NULL;
-
-  if (pending == NULL)
-    {
-      /* Not cached and no pending request, create it */
-      *request = g_new0 (AsyncTextureLoadData, 1);
-      g_hash_table_insert (cache->priv->outstanding_requests, cache_key_dup (key), *request);
-    }
-  else
-   *request = pending;
-
-  /* Regardless of whether there was a pending request, prepend our texture here. */
-  (*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (*texture));
-
-  return had_pending;
-}
-
-/**
- * shell_texture_cache_load_gicon:
- *
- * This method returns a new #ClutterClone for a given #GIcon.  If the
- * icon isn't loaded already, the texture will be filled asynchronously.
- *
- * Return Value: (transfer none): A new #ClutterActor for the icon
- */
-ClutterActor *
-shell_texture_cache_load_gicon (ShellTextureCache *cache,
-                                GIcon             *icon,
-                                gint               size)
-{
-  AsyncTextureLoadData *request;
-  ClutterActor *texture;
-  CacheKey key;
-  GtkIconTheme *theme;
-  GtkIconInfo *info;
-
-  memset (&key, 0, sizeof(key));
-  key.icon = icon;
-  key.size = size;
-
-  if (create_texture_and_ensure_request (cache, &key, &request, &texture))
-    return texture;
-
-  /* Do theme lookups in the main thread to avoid thread-unsafety */
-  theme = gtk_icon_theme_get_default ();
-
-  info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
-  if (info != NULL)
-    {
-      /* hardcoded here for now; we should actually blow this away on
-       * icon theme changes probably */
-      request->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
-      request->icon = g_object_ref (icon);
-      request->icon_info = info;
-      request->width = request->height = size;
-
-      load_icon_pixbuf_async (cache, icon, info, size, NULL, on_pixbuf_loaded, request);
-    }
-  else
-    {
-      /* Blah; we failed to find the icon, but we've added our texture to the outstanding
-       * requests.  In that case, just undo what create_texture_lookup_status did.
-       */
-       g_slist_foreach (request->textures, (GFunc) g_object_unref, NULL);
-       g_slist_free (request->textures);
-       g_free (request);
-       g_hash_table_remove (cache->priv->outstanding_requests, &key);
-    }
-
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_load_icon_name:
- * @cache: The texture cache instance
- * @name: Name of a themed icon
- * @size: Size of themed
- *
- * Load a themed icon into a texture.
- *
- * Return Value: (transfer none): A new #ClutterTexture for the icon
- */
-ClutterActor *
-shell_texture_cache_load_icon_name (ShellTextureCache *cache,
-                                    const char        *name,
-                                    gint               size)
-{
-  ClutterActor *texture;
-  GIcon *themed;
-
-  themed = g_themed_icon_new (name);
-  texture = shell_texture_cache_load_gicon (cache, themed, size);
-  g_object_unref (themed);
-
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_load_uri_async:
- *
- * @cache: The texture cache instance
- * @uri: uri of the image file from which to create a pixbuf
- * @available_width: available width for the image, can be -1 if not limited
- * @available_height: available height for the image, can be -1 if not limited
- *
- * Asynchronously load an image.   Initially, the returned texture will have a natural
- * size of zero.  At some later point, either the image will be loaded successfully
- * and at that point size will be negotiated, or upon an error, no image will be set.
- *
- * Return value: (transfer none): A new #ClutterActor with no image loaded initially.
- */
-ClutterActor *
-shell_texture_cache_load_uri_async (ShellTextureCache *cache,
-                                    const gchar *uri,
-                                    int available_width,
-                                    int available_height)
-{
-  ClutterTexture *texture;
-  AsyncTextureLoadData *data;
-
-  texture = create_default_texture (cache);
-
-  data = g_new0 (AsyncTextureLoadData, 1);
-  data->policy = SHELL_TEXTURE_CACHE_POLICY_NONE;
-  data->uri = g_strdup (uri);
-  data->width = available_width;
-  data->height = available_height;
-  data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
-  load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data);
-
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_load_uri_sync:
- *
- * @cache: The texture cache instance
- * @policy: Requested lifecycle of cached data
- * @uri: uri of the image file from which to create a pixbuf
- * @available_width: available width for the image, can be -1 if not limited
- * @available_height: available height for the image, can be -1 if not limited
- * @error: Return location for error
- *
- * Synchronously load an image from a uri.  The image is scaled down to fit the
- * available width and height imensions, but the image is never scaled up beyond
- * its actual size. The pixbuf is rotated according to the associated orientation
- * setting.
- *
- * Return value: (transfer none): A new #ClutterActor with the image file loaded if it was
- *               generated succesfully, %NULL otherwise
- */
-ClutterActor *
-shell_texture_cache_load_uri_sync (ShellTextureCache *cache,
-                                   ShellTextureCachePolicy policy,
-                                   const gchar       *uri,
-                                   int                available_width,
-                                   int                available_height,
-                                   GError            **error)
-{
-  ClutterTexture *texture;
-  CoglHandle texdata;
-  GdkPixbuf *pixbuf;
-  CacheKey key;
-
-  texture = create_default_texture (cache);
-
-  memset (&key, 0, sizeof (CacheKey));
-  key.policy = policy;
-  key.uri = (char*)uri;
-  key.size = available_width;
-  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
-
-  if (texdata == NULL)
-    {
-      pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
-      if (!pixbuf)
-        {
-          g_object_unref (texture);
-          return NULL;
-        }
-
-      texdata = pixbuf_to_cogl_handle (pixbuf);
-      g_object_unref (pixbuf);
-
-      set_texture_cogl_texture (texture, texdata);
-
-      if (policy == SHELL_TEXTURE_CACHE_POLICY_FOREVER)
-        {
-          g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
-        }
-      else
-        cogl_handle_unref (texdata);
-    }
-  else
-    set_texture_cogl_texture (texture, texdata);
-
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_load_from_data:
- * @cache: The texture cache instance
- * @data: Image data in PNG, GIF, etc format
- * @len: length of @data
- * @size: Size in pixels to use for the resulting texture
- * @error: Return location for error
- *
- * Synchronously creates an image from @data. The image is scaled down
- * to fit the available width and height dimensions, but the image is
- * never scaled up beyond its actual size. The pixbuf is rotated
- * according to the associated orientation setting.
- *
- * Return value: (transfer none): A new #ClutterActor with the image data loaded if it was
- *               generated succesfully, %NULL otherwise
- */
-ClutterActor *
-shell_texture_cache_load_from_data (ShellTextureCache *cache,
-                                    const guchar      *data,
-                                    gsize              len,
-                                    int                size,
-                                    GError           **error)
-{
-  ClutterTexture *texture;
-  CoglHandle texdata;
-  GdkPixbuf *pixbuf;
-  CacheKey key;
-  gchar *checksum;
-
-  texture = create_default_texture (cache);
-  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
-
-  checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len);
-
-  memset (&key, 0, sizeof(key));
-  key.size = size;
-  key.checksum = checksum;
-
-  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
-  if (texdata == NULL)
-    {
-      pixbuf = impl_load_pixbuf_data (data, len, size, size, error);
-      if (!pixbuf)
-        {
-          g_object_unref (texture);
-          return NULL;
-        }
-
-      texdata = pixbuf_to_cogl_handle (pixbuf);
-      g_object_unref (pixbuf);
-
-      set_texture_cogl_texture (texture, texdata);
-
-      g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
-    }
-
-  g_free (key.checksum);
-
-  set_texture_cogl_texture (texture, texdata);
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_load_from_raw:
- * @cache: a #ShellTextureCache
- * @data: raw pixel data
- * @len: the length of @data
- * @has_alpha: whether @data includes an alpha channel
- * @width: width in pixels of @data
- * @height: width in pixels of @data
- * @rowstride: rowstride of @data
- * @size: size of icon to return
- *
- * Creates (or retrieves from cache) an icon based on raw pixel data.
- *
- * Return value: (transfer none): a new #ClutterActor displaying a
- * pixbuf created from @data and the other parameters.
- **/
-ClutterActor *
-shell_texture_cache_load_from_raw (ShellTextureCache *cache,
-                                   const guchar      *data,
-                                   gsize              len,
-                                   gboolean           has_alpha,
-                                   int                width,
-                                   int                height,
-                                   int                rowstride,
-                                   int                size,
-                                   GError           **error)
-{
-  ClutterTexture *texture;
-  CoglHandle texdata;
-  CacheKey key;
-  gchar *checksum;
-
-  texture = create_default_texture (cache);
-  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
-
-  /* In theory, two images of different size could have the same
-   * pixel data. We ignore that theory.
-   */
-  checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len);
-
-  memset (&key, 0, sizeof(key));
-  key.size = size;
-  key.checksum = checksum;
-
-  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
-  if (texdata == NULL)
-    {
-      texdata = cogl_texture_new_from_data (width, height, COGL_TEXTURE_NONE,
-                                            has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
-                                            COGL_PIXEL_FORMAT_ANY,
-                                            rowstride, data);
-      g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
-    }
-
-  g_free (key.checksum);
-
-  set_texture_cogl_texture (texture, texdata);
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_load_thumbnail:
- * @cache:
- * @size: Size in pixels to use for thumbnail
- * @uri: Source URI
- * @mimetype: Source mime type
- *
- * Asynchronously load a thumbnail image of a URI into a texture.  The
- * returned texture object will be a new instance; however, its texture data
- * may be shared with other objects.  This implies the texture data is cached.
- *
- * The current caching policy is permanent; to uncache, you must explicitly
- * call shell_texture_cache_unref_thumbnail().
- *
- * Returns: (transfer none): A new #ClutterActor
- */
-ClutterActor *
-shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
-                                    int                size,
-                                    const char        *uri,
-                                    const char        *mimetype)
-{
-  ClutterTexture *texture;
-  AsyncTextureLoadData *data;
-  CacheKey key;
-  CoglHandle texdata;
-
-  /* Don't attempt to load thumbnails for non-local URIs */
-  if (!g_str_has_prefix (uri, "file://"))
-    {
-      GIcon *icon = icon_for_mimetype (mimetype);
-      return shell_texture_cache_load_gicon (cache, icon, size);
-    }
-
-  texture = create_default_texture (cache);
-  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
-
-  memset (&key, 0, sizeof(key));
-  key.size = size;
-  key.thumbnail_uri = (char*)uri;
-
-  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
-  if (!texdata)
-    {
-      data = g_new0 (AsyncTextureLoadData, 1);
-      data->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
-      data->uri = g_strdup (uri);
-      data->mimetype = g_strdup (mimetype);
-      data->thumbnail = TRUE;
-      data->width = size;
-      data->height = size;
-      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
-      load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data);
-    }
-  else
-    {
-      set_texture_cogl_texture (texture, texdata);
-    }
-
-  return CLUTTER_ACTOR (texture);
-}
-
-static GIcon *
-icon_for_recent (GtkRecentInfo *info)
-{
-  const char *mimetype;
-
-  mimetype = gtk_recent_info_get_mime_type (info);
-  if (!mimetype)
-    {
-      return g_themed_icon_new (GTK_STOCK_FILE);
-    }
-
-  return icon_for_mimetype (mimetype);
-}
-
-/**
- * shell_texture_cache_load_recent_thumbnail:
- * @cache:
- * @size: Size in pixels to use for thumbnail
- * @info: Recent item info
- *
- * Asynchronously load a thumbnail image of a #GtkRecentInfo into a texture.  The
- * returned texture object will be a new instance; however, its texture data
- * may be shared with other objects.  This implies the texture data is cached.
- *
- * The current caching policy is permanent; to uncache, you must explicitly
- * call shell_texture_cache_unref_recent_thumbnail().
- *
- * Returns: (transfer none): A new #ClutterActor
- */
-ClutterActor *
-shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cache,
-                                           int                size,
-                                           GtkRecentInfo     *info)
-{
-  ClutterTexture *texture;
-  AsyncTextureLoadData *data;
-  CacheKey key;
-  CoglHandle texdata;
-  const char *uri;
-
-  uri = gtk_recent_info_get_uri (info);
-
-  /* Don't attempt to load thumbnails for non-local URIs */
-  if (!g_str_has_prefix (uri, "file://"))
-    {
-      GIcon *icon = icon_for_recent (info);
-      return shell_texture_cache_load_gicon (cache, icon, size);
-    }
-
-  texture = CLUTTER_TEXTURE (clutter_texture_new ());
-  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
-
-  memset (&key, 0, sizeof(key));
-  key.size = size;
-  key.thumbnail_uri = (char*)gtk_recent_info_get_uri (info);
-
-  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
-  if (!texdata)
-    {
-      data = g_new0 (AsyncTextureLoadData, 1);
-      data->policy = SHELL_TEXTURE_CACHE_POLICY_FOREVER;
-      data->thumbnail = TRUE;
-      data->recent_info = gtk_recent_info_ref (info);
-      data->width = size;
-      data->height = size;
-      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
-      load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data);
-    }
-  else
-    {
-      set_texture_cogl_texture (texture, texdata);
-    }
-
-  return CLUTTER_ACTOR (texture);
-}
-
-/**
- * shell_texture_cache_evict_thumbnail:
- * @cache:
- * @uri: Source URI
- *
- * Removes all references added by shell_texture_cache_load_thumbnail() function
- * created for the given URI.
- */
-void
-shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
-                                     const char        *uri)
-{
-  GHashTableIter iter;
-  gpointer key, value;
-
-  g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
-
-  while (g_hash_table_iter_next (&iter, &key, &value))
-    {
-      CacheKey *cachekey = key;
-
-      if (cachekey->thumbnail_uri == NULL || strcmp (cachekey->thumbnail_uri, uri) != 0)
-        continue;
-
-      g_hash_table_iter_remove (&iter);
-    }
-}
-
-/**
- * shell_texture_cache_evict_recent_thumbnail:
- * @cache:
- * @info: A recent info
- *
- * Removes all references added by shell_texture_cache_load_recent_thumbnail() function
- * for the URI associated with the given @info.
- */
-void
-shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
-                                            GtkRecentInfo     *info)
-{
-  shell_texture_cache_evict_thumbnail (cache, gtk_recent_info_get_uri (info));
-}
-
-static size_t
-pixbuf_byte_size (GdkPixbuf *pixbuf)
-{
-  /* This bit translated from gtk+/gdk-pixbuf/gdk-pixbuf.c:gdk_pixbuf_copy.  The comment
-   * there was:
-   *
-   * Calculate a semi-exact size.  Here we copy with full rowstrides;
-   * maybe we should copy each row individually with the minimum
-   * rowstride?
-   */
-  return (gdk_pixbuf_get_height (pixbuf) - 1) * gdk_pixbuf_get_rowstride (pixbuf) +
-    + gdk_pixbuf_get_width (pixbuf) * ((gdk_pixbuf_get_n_channels (pixbuf)* gdk_pixbuf_get_bits_per_sample (pixbuf) + 7) / 8);
-}
-
-/**
- * shell_texture_cache_pixbuf_equal:
- *
- * Returns: %TRUE iff the given pixbufs are bytewise-equal
- */
-gboolean
-shell_texture_cache_pixbuf_equal (ShellTextureCache *cache, GdkPixbuf *a, GdkPixbuf *b)
-{
-  size_t size_a = pixbuf_byte_size (a);
-  size_t size_b = pixbuf_byte_size (b);
-  if (size_a != size_b)
-    return FALSE;
-  return memcmp (gdk_pixbuf_get_pixels (a), gdk_pixbuf_get_pixels (b), size_a) == 0;
-}
-
-static ShellTextureCache *instance = NULL;
-
-/**
- * shell_texture_cache_get_default:
- *
- * Return value: (transfer none): The global texture cache
- */
-ShellTextureCache*
-shell_texture_cache_get_default (void)
-{
-  if (instance == NULL)
-    instance = g_object_new (SHELL_TYPE_TEXTURE_CACHE, NULL);
-  return instance;
-}
diff --git a/src/shell-texture-cache.h b/src/shell-texture-cache.h
deleted file mode 100644 (file)
index d7280c4..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-#ifndef __SHELL_TEXTURE_CACHE_H__
-#define __SHELL_TEXTURE_CACHE_H__
-
-#include <gio/gio.h>
-#include <gtk/gtk.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
-#include <clutter/clutter.h>
-
-#define SHELL_TYPE_TEXTURE_CACHE                 (shell_texture_cache_get_type ())
-#define SHELL_TEXTURE_CACHE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_TEXTURE_CACHE, ShellTextureCache))
-#define SHELL_TEXTURE_CACHE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), SHELL_TYPE_TEXTURE_CACHE, ShellTextureCacheClass))
-#define SHELL_IS_TEXTURE_CACHE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_TEXTURE_CACHE))
-#define SHELL_IS_TEXTURE_CACHE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), SHELL_TYPE_TEXTURE_CACHE))
-#define SHELL_TEXTURE_CACHE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), SHELL_TYPE_TEXTURE_CACHE, ShellTextureCacheClass))
-
-typedef struct _ShellTextureCache ShellTextureCache;
-typedef struct _ShellTextureCacheClass ShellTextureCacheClass;
-
-typedef struct _ShellTextureCachePrivate ShellTextureCachePrivate;
-
-struct _ShellTextureCache
-{
-  GObject parent;
-
-  ShellTextureCachePrivate *priv;
-};
-
-struct _ShellTextureCacheClass
-{
-  GObjectClass parent_class;
-
-};
-
-typedef enum {
-  SHELL_TEXTURE_CACHE_POLICY_NONE,
-  SHELL_TEXTURE_CACHE_POLICY_FOREVER
-} ShellTextureCachePolicy;
-
-GType shell_texture_cache_get_type (void) G_GNUC_CONST;
-
-ShellTextureCache* shell_texture_cache_get_default (void);
-
-ClutterActor *shell_texture_cache_bind_pixbuf_property (ShellTextureCache *cache,
-                                                        GObject           *object,
-                                                        const char        *property_name);
-
-ClutterActor *shell_texture_cache_load_icon_name (ShellTextureCache *cache,
-                                                  const char        *name,
-                                                  gint               size);
-
-ClutterActor *shell_texture_cache_load_gicon (ShellTextureCache *cache,
-                                              GIcon             *icon,
-                                              gint               size);
-
-ClutterActor *shell_texture_cache_load_thumbnail (ShellTextureCache *cache,
-                                                  int                size,
-                                                  const char        *uri,
-                                                  const char        *mimetype);
-
-ClutterActor *shell_texture_cache_load_recent_thumbnail (ShellTextureCache *cache,
-                                                         int                size,
-                                                         GtkRecentInfo     *info);
-
-void shell_texture_cache_evict_thumbnail (ShellTextureCache *cache,
-                                          const char        *uri);
-
-void shell_texture_cache_evict_recent_thumbnail (ShellTextureCache *cache,
-                                                 GtkRecentInfo     *info);
-
-ClutterActor *shell_texture_cache_load_uri_async (ShellTextureCache *cache,
-                                                  const gchar       *filename,
-                                                  int                available_width,
-                                                  int                available_height);
-
-ClutterActor *shell_texture_cache_load_uri_sync (ShellTextureCache *cache,
-                                                 ShellTextureCachePolicy policy,
-                                                 const gchar       *filename,
-                                                 int                available_width,
-                                                 int                available_height,
-                                                 GError           **error);
-
-ClutterActor *shell_texture_cache_load_from_data (ShellTextureCache *cache,
-                                                  const guchar      *data,
-                                                  gsize              len,
-                                                  int                size,
-                                                  GError           **error);
-ClutterActor *shell_texture_cache_load_from_raw  (ShellTextureCache *cache,
-                                                  const guchar      *data,
-                                                  gsize              len,
-                                                  gboolean           has_alpha,
-                                                  int                width,
-                                                  int                height,
-                                                  int                rowstride,
-                                                  int                size,
-                                                  GError           **error);
-
-gboolean shell_texture_cache_pixbuf_equal (ShellTextureCache *cache, GdkPixbuf *a, GdkPixbuf *b);
-
-#endif /* __SHELL_TEXTURE_CACHE_H__ */
index 547c34147aca65df4c1932a0afce0ac9f8662bec..2dd36b9c3a16386a70b043fe3ac1dd9e8cc91f34 100644 (file)
@@ -16,7 +16,7 @@
 #include "shell-window-tracker.h"
 #include "shell-app-system.h"
 #include "shell-app-private.h"
-#include "shell-texture-cache.h"
+#include "st/st-texture-cache.h"
 #include "shell-global.h"
 #include "shell-marshal.h"
 
@@ -847,8 +847,8 @@ shell_startup_sequence_create_icon (ShellStartupSequence *sequence, guint size)
     }
 
   themed = g_themed_icon_new (icon_name);
-  texture = shell_texture_cache_load_gicon (shell_texture_cache_get_default (),
-                                            themed, size);
+  texture = st_texture_cache_load_gicon (st_texture_cache_get_default (),
+                                         themed, size);
   g_object_unref (G_OBJECT (themed));
   return texture;
 }
index 6cb3cbfc8a03c7b4d5e5790c81ae55bc2612b685..e4749028e3c505833d7add87a142eb444b70111d 100644 (file)
@@ -842,9 +842,7 @@ _st_entry_set_icon_from_file (StEntry       *entry,
 
       cache = st_texture_cache_get_default ();
 
-
-
-      *icon = (ClutterActor*) st_texture_cache_get_texture (cache, filename);
+      *icon = (ClutterActor*) st_texture_cache_load_file_simple (cache, filename);
 
       clutter_actor_set_reactive (*icon, TRUE);
       clutter_actor_set_parent (*icon, CLUTTER_ACTOR (entry));
index 6fca08dd79205b2decacf8030ce68936e8bff41b..ddf8946584824222fb5c200d4b17695fb8b11263 100644 (file)
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * st-widget.h: Base class for St actors
- *
- * Copyright 2007 OpenedHand
- * Copyright 2009 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- * Boston, MA 02111-1307, USA.
- *
- */
 
-/**
- * SECTION:st-texture-cache
- * @short_description: A per-process store to cache textures
- *
- * #StTextureCache allows an application to re-use an previously loaded
- * textures.
- */
-
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
-#include <glib.h>
-#include <glib-object.h>
-#include <gdk-pixbuf/gdk-pixbuf.h>
+#include "st-texture-cache.h"
+#include <gtk/gtk.h>
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <libgnomeui/gnome-desktop-thumbnail.h>
 #include <string.h>
+#include <glib.h>
 
-#include "st-texture-cache.h"
-#include "st-marshal.h"
-#include "st-private.h"
-#include "st-subtexture.h"
-G_DEFINE_TYPE (StTextureCache, st_texture_cache, G_TYPE_OBJECT)
+typedef struct
+{
+  StTextureCachePolicy policy;
 
-#define TEXTURE_CACHE_PRIVATE(o) \
-  (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_TEXTURE_CACHE, StTextureCachePrivate))
+  /* These are exclusive */
+  GIcon *icon;
+  gchar *uri;
+  gchar *thumbnail_uri;
+  gchar *checksum;
 
-typedef struct _StTextureCachePrivate StTextureCachePrivate;
+  /* This one is common to all */
+  guint size;
+} CacheKey;
 
 struct _StTextureCachePrivate
 {
-  GHashTable *cache;
+  /* Things that were loaded with a cache policy != NONE */
+  GHashTable *keyed_cache; /* CacheKey -> CoglTexture* */
+  /* Presently this is used to de-duplicate requests for GIcons,
+   * it could in theory be extended to async URL loading and other
+   * cases too.
+   */
+  GHashTable *outstanding_requests; /* CacheKey -> AsyncTextureLoadData * */
+  GnomeDesktopThumbnailFactory *thumbnails;
 };
 
-typedef struct FinalizedClosure
+static void st_texture_cache_dispose (GObject *object);
+static void st_texture_cache_finalize (GObject *object);
+
+G_DEFINE_TYPE(StTextureCache, st_texture_cache, G_TYPE_OBJECT);
+
+static guint
+cache_key_hash (gconstpointer a)
 {
-  gchar          *path;
-  StTextureCache *cache;
-} FinalizedClosure;
+  CacheKey *akey = (CacheKey *)a;
+  guint base_hash;
+
+  if (akey->icon)
+    base_hash = g_icon_hash (akey->icon);
+  else if (akey->uri)
+    base_hash = g_str_hash (akey->uri);
+  else if (akey->thumbnail_uri)
+    base_hash = g_str_hash (akey->thumbnail_uri);
+  else if (akey->checksum)
+    base_hash = g_str_hash (akey->checksum);
+  else
+    g_assert_not_reached ();
+  return base_hash + 31*akey->size;
+}
 
-enum
+static gboolean
+cache_key_equal (gconstpointer a,
+                 gconstpointer b)
 {
-  PROP_0,
-};
+  CacheKey *akey = (CacheKey*)a;
+  CacheKey *bkey = (CacheKey*)b;
+
+  /* We don't compare policy here, since we need
+   * a way to look up a cache key without respect to
+   * the policy. */
+
+  if (akey->size != bkey->size)
+    return FALSE;
+
+  if (akey->icon && bkey->icon)
+    return g_icon_equal (akey->icon, bkey->icon);
+  else if (akey->uri && bkey->uri)
+    return strcmp (akey->uri, bkey->uri) == 0;
+  else if (akey->thumbnail_uri && bkey->thumbnail_uri)
+    return strcmp (akey->thumbnail_uri, bkey->thumbnail_uri) == 0;
+  else if (akey->checksum && bkey->checksum)
+    return strcmp (akey->checksum, bkey->checksum) == 0;
+
+  return FALSE;
+}
 
-static StTextureCache* __cache_singleton = NULL;
+static CacheKey *
+cache_key_dup (CacheKey *key)
+{
+  CacheKey *ret = g_new0 (CacheKey, 1);
+  ret->policy = key->policy;
+  if (key->icon)
+    ret->icon = g_object_ref (key->icon);
+  ret->uri = g_strdup (key->uri);
+  ret->thumbnail_uri = g_strdup (key->thumbnail_uri);
+  ret->checksum = g_strdup (key->checksum);
+  ret->size = key->size;
+  return ret;
+}
+
+static void
+cache_key_destroy (gpointer a)
+{
+  CacheKey *akey = (CacheKey*)a;
+  if (akey->icon)
+    g_object_unref (akey->icon);
+  g_free (akey->uri);
+  g_free (akey->thumbnail_uri);
+  g_free (akey->checksum);
+  g_free (akey);
+}
 
-/*
- * Convention: posX with a value of -1 indicates whole texture
- */
-typedef struct StTextureCacheItem {
-  char          filename[256];
-  int           width, height;
-  int           posX, posY;
-  ClutterActor *ptr;
-} StTextureCacheItem;
 
-static StTextureCacheItem *
-st_texture_cache_item_new (void)
+/* We want to preserve the aspect ratio by default, also the default
+ * material for an empty texture is full opacity white, which we
+ * definitely don't want.  Skip that by setting 0 opacity.
+ */
+static ClutterTexture *
+create_default_texture (StTextureCache *self)
 {
-  return g_slice_new0 (StTextureCacheItem);
+  ClutterTexture * texture = CLUTTER_TEXTURE (clutter_texture_new ());
+  g_object_set (texture, "keep-aspect-ratio", TRUE, "opacity", 0, NULL);
+  return texture;
 }
 
+/* Reverse the opacity we added while loading */
 static void
-st_texture_cache_item_free (StTextureCacheItem *item)
+set_texture_cogl_texture (ClutterTexture *clutter_texture, CoglHandle cogl_texture)
 {
-  g_slice_free (StTextureCacheItem, item);
+  clutter_texture_set_cogl_texture (clutter_texture, cogl_texture);
+  g_object_set (clutter_texture, "opacity", 255, NULL);
 }
 
 static void
-st_texture_cache_set_property (GObject      *object,
-                               guint         prop_id,
-                               const GValue *value,
-                               GParamSpec   *pspec)
+st_texture_cache_class_init (StTextureCacheClass *klass)
 {
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  GObjectClass *gobject_class = (GObjectClass *)klass;
+
+  gobject_class->dispose = st_texture_cache_dispose;
+  gobject_class->finalize = st_texture_cache_finalize;
 }
 
 static void
-st_texture_cache_get_property (GObject    *object,
-                               guint       prop_id,
-                               GValue     *value,
-                               GParamSpec *pspec)
+st_texture_cache_init (StTextureCache *self)
 {
-  switch (prop_id)
-    {
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-      break;
-    }
+  self->priv = g_new0 (StTextureCachePrivate, 1);
+  self->priv->keyed_cache = g_hash_table_new_full (cache_key_hash, cache_key_equal,
+                                                   cache_key_destroy, cogl_handle_unref);
+  self->priv->outstanding_requests = g_hash_table_new_full (cache_key_hash, cache_key_equal,
+                                                            cache_key_destroy, NULL);
+  self->priv->thumbnails = gnome_desktop_thumbnail_factory_new (GNOME_DESKTOP_THUMBNAIL_SIZE_NORMAL);
 }
 
 static void
 st_texture_cache_dispose (GObject *object)
 {
-  if (G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose)
-    G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
+  StTextureCache *self = (StTextureCache*)object;
+
+  if (self->priv->keyed_cache)
+    g_hash_table_destroy (self->priv->keyed_cache);
+  self->priv->keyed_cache = NULL;
+
+  if (self->priv->thumbnails)
+    g_object_unref (self->priv->thumbnails);
+  self->priv->thumbnails = NULL;
+
+  G_OBJECT_CLASS (st_texture_cache_parent_class)->dispose (object);
 }
 
 static void
 st_texture_cache_finalize (GObject *object)
 {
-  StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(object);
+  G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
+}
+
+typedef struct {
+  StTextureCache *cache;
+  char *uri;
+  char *mimetype;
+  gboolean thumbnail;
+  GIcon *icon;
+  GtkRecentInfo *recent_info;
+  GtkIconInfo *icon_info;
+  gint width;
+  gint height;
+  gpointer user_data;
+} AsyncIconLookupData;
+
+static gboolean
+compute_pixbuf_scale (gint      width,
+                      gint      height,
+                      gint      available_width,
+                      gint      available_height,
+                      gint     *new_width,
+                      gint     *new_height)
+{
+  int scaled_width, scaled_height;
+
+  if (width == 0 || height == 0)
+    return FALSE;
 
-  if (priv->cache)
+  if (available_width >= 0 && available_height >= 0)
+    {
+      // This should keep the aspect ratio of the image intact, because if
+      // available_width < (available_height * width) / height
+      // than
+      // (available_width * height) / width < available_height
+      // So we are guaranteed to either scale the image to have an available_width
+      // for width and height scaled accordingly OR have the available_height
+      // for height and width scaled accordingly, whichever scaling results
+      // in the image that can fit both available dimensions.
+      scaled_width = MIN (available_width, (available_height * width) / height);
+      scaled_height = MIN (available_height, (available_width * height) / width);
+    }
+  else if (available_width >= 0)
+    {
+      scaled_width = available_width;
+      scaled_height = (available_width * height) / width;
+    }
+  else if (available_height >= 0)
     {
-      g_hash_table_unref (priv->cache);
-      priv->cache = NULL;
+      scaled_width = (available_height * width) / height;
+      scaled_height = available_height;
+    }
+  else
+    {
+      scaled_width = scaled_height = 0;
     }
 
-  G_OBJECT_CLASS (st_texture_cache_parent_class)->finalize (object);
+  // Scale the image only if that will not increase its original dimensions.
+  if (scaled_width > 0 && scaled_height > 0 && scaled_width < width && scaled_height < height)
+    {
+      *new_width = scaled_width;
+      *new_height = scaled_height;
+      return TRUE;
+    }
+  return FALSE;
 }
 
-static void
-st_texture_cache_class_init (StTextureCacheClass *klass)
+static GdkPixbuf *
+impl_load_pixbuf_gicon (GIcon       *icon,
+                        GtkIconInfo *info,
+                        int          size,
+                        GError     **error)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  int scaled_width, scaled_height;
+  GdkPixbuf *pixbuf = gtk_icon_info_load_icon (info, error);
+  int width, height;
 
-  g_type_class_add_private (klass, sizeof (StTextureCachePrivate));
+  if (!pixbuf)
+    return NULL;
 
-  object_class->get_property = st_texture_cache_get_property;
-  object_class->set_property = st_texture_cache_set_property;
-  object_class->dispose = st_texture_cache_dispose;
-  object_class->finalize = st_texture_cache_finalize;
+  width = gdk_pixbuf_get_width (pixbuf);
+  height = gdk_pixbuf_get_height (pixbuf);
 
+  if (compute_pixbuf_scale (width,
+                            height,
+                            size, size,
+                            &scaled_width, &scaled_height))
+    {
+      GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR);
+      g_object_unref (pixbuf);
+      pixbuf = scaled;
+    }
+  return pixbuf;
 }
 
+// A private structure for keeping width and height.
+typedef struct {
+  int width;
+  int height;
+} Dimensions;
+
 static void
-st_texture_cache_init (StTextureCache *self)
+icon_lookup_data_destroy (gpointer p)
 {
-  StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
-
-  priv->cache = g_hash_table_new_full (g_str_hash,
-                                       g_str_equal,
-                                       g_free,
-                                       NULL);
+  AsyncIconLookupData *data = p;
 
+  if (data->icon)
+    {
+      g_object_unref (data->icon);
+      gtk_icon_info_free (data->icon_info);
+    }
+  else if (data->uri)
+    g_free (data->uri);
+  if (data->mimetype)
+    g_free (data->mimetype);
+  if (data->recent_info)
+    gtk_recent_info_unref (data->recent_info);
+
+  g_free (data);
 }
 
 /**
- * st_texture_cache_get_default:
+ * on_image_size_prepared:
  *
- * Returns the default texture cache. This is owned by St and should not be
- * unreferenced or freed.
+ * @pixbuf_loader: #GdkPixbufLoader loading the image
+ * @width: the original width of the image
+ * @height: the original height of the image
+ * @data: pointer to the #Dimensions sructure containing available width and height for the image,
+ *        available width or height can be -1 if the dimension is not limited
  *
- * Returns: (transfer none): a StTextureCache
+ * Private function.
+ *
+ * Sets the size of the image being loaded to fit the available width and height dimensions,
+ * but never scales up the image beyond its actual size.
+ * Intended to be used as a callback for #GdkPixbufLoader "size-prepared" signal.
  */
-StTextureCache*
-st_texture_cache_get_default (void)
+static void
+on_image_size_prepared (GdkPixbufLoader *pixbuf_loader,
+                        gint             width,
+                        gint             height,
+                        gpointer         data)
+{
+  Dimensions *available_dimensions = data;
+  int available_width = available_dimensions->width;
+  int available_height = available_dimensions->height;
+  int scaled_width;
+  int scaled_height;
+
+  if (compute_pixbuf_scale (width, height, available_width, available_height,
+                            &scaled_width, &scaled_height))
+    gdk_pixbuf_loader_set_size (pixbuf_loader, scaled_width, scaled_height);
+}
+
+static GdkPixbuf *
+impl_load_pixbuf_data (const guchar   *data,
+                       gsize           size,
+                       int             available_width,
+                       int             available_height,
+                       GError        **error)
+{
+  GdkPixbufLoader *pixbuf_loader = NULL;
+  GdkPixbuf *rotated_pixbuf = NULL;
+  GdkPixbuf *pixbuf;
+  gboolean success;
+  Dimensions available_dimensions;
+  int width_before_rotation, width_after_rotation;
+
+  pixbuf_loader = gdk_pixbuf_loader_new ();
+
+  available_dimensions.width = available_width;
+  available_dimensions.height = available_height;
+  g_signal_connect (pixbuf_loader, "size-prepared",
+                    G_CALLBACK (on_image_size_prepared), &available_dimensions);
+
+  success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
+  if (!success)
+    goto out;
+  success = gdk_pixbuf_loader_close (pixbuf_loader, error);
+  if (!success)
+    goto out;
+
+  pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
+
+  width_before_rotation = gdk_pixbuf_get_width (pixbuf);
+
+  rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+  width_after_rotation = gdk_pixbuf_get_width (rotated_pixbuf);
+
+  // There is currently no way to tell if the pixbuf will need to be rotated before it is loaded,
+  // so we only check that once it is loaded, and reload it again if it needs to be rotated in order
+  // to use the available width and height correctly.
+  // http://bugzilla.gnome.org/show_bug.cgi?id=579003
+  if (width_before_rotation != width_after_rotation)
+    {
+      g_object_unref (pixbuf_loader);
+      g_object_unref (rotated_pixbuf);
+      rotated_pixbuf = NULL;
+
+      pixbuf_loader = gdk_pixbuf_loader_new ();
+
+      // We know that the image will later be rotated, so we reverse the available dimensions.
+      available_dimensions.width = available_height;
+      available_dimensions.height = available_width;
+      g_signal_connect (pixbuf_loader, "size-prepared",
+                        G_CALLBACK (on_image_size_prepared), &available_dimensions);
+
+      success = gdk_pixbuf_loader_write (pixbuf_loader, data, size, error);
+      if (!success)
+        goto out;
+
+      success = gdk_pixbuf_loader_close (pixbuf_loader, error);
+      if (!success)
+        goto out;
+
+      pixbuf = gdk_pixbuf_loader_get_pixbuf (pixbuf_loader);
+
+      rotated_pixbuf = gdk_pixbuf_apply_embedded_orientation (pixbuf);
+    }
+
+out:
+  if (pixbuf_loader)
+    g_object_unref (pixbuf_loader);
+  return rotated_pixbuf;
+}
+
+static GdkPixbuf *
+impl_load_pixbuf_file (const char     *uri,
+                       int             available_width,
+                       int             available_height,
+                       GError        **error)
+{
+  GdkPixbuf *pixbuf = NULL;
+  GFile *file;
+  char *contents = NULL;
+  gsize size;
+
+  file = g_file_new_for_uri (uri);
+  if (g_file_load_contents (file, NULL, &contents, &size, NULL, error))
+    {
+      pixbuf = impl_load_pixbuf_data ((const guchar *) contents, size,
+                                      available_width, available_height,
+                                      error);
+    }
+
+  g_object_unref (file);
+  g_free (contents);
+
+  return pixbuf;
+}
+
+static GdkPixbuf *
+impl_load_thumbnail (StTextureCache    *cache,
+                     const char        *uri,
+                     const char        *mime_type,
+                     guint              size,
+                     GError           **error)
+{
+  GnomeDesktopThumbnailFactory *thumbnail_factory;
+  GdkPixbuf *pixbuf = NULL;
+  GFile *file;
+  GFileInfo *file_info;
+  GTimeVal mtime_g;
+  time_t mtime = 0;
+  char *existing_thumbnail;
+
+  file = g_file_new_for_uri (uri);
+  file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+  g_object_unref (file);
+  if (file_info)
+    {
+      g_file_info_get_modification_time (file_info, &mtime_g);
+      g_object_unref (file_info);
+      mtime = (time_t) mtime_g.tv_sec;
+    }
+
+  thumbnail_factory = cache->priv->thumbnails;
+
+  existing_thumbnail = gnome_desktop_thumbnail_factory_lookup (thumbnail_factory, uri, mtime);
+
+  if (existing_thumbnail != NULL)
+    {
+      pixbuf = gdk_pixbuf_new_from_file_at_size (existing_thumbnail, size, size, error);
+      g_free (existing_thumbnail);
+    }
+  else if (gnome_desktop_thumbnail_factory_has_valid_failed_thumbnail (thumbnail_factory, uri, mtime))
+    g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Has failed thumbnail");
+  else if (gnome_desktop_thumbnail_factory_can_thumbnail (thumbnail_factory, uri, mime_type, mtime))
+    {
+      pixbuf = gnome_desktop_thumbnail_factory_generate_thumbnail (thumbnail_factory, uri, mime_type);
+      if (pixbuf)
+        {
+          // we need to save the thumbnail so that we don't need to generate it again in the future
+          gnome_desktop_thumbnail_factory_save_thumbnail (thumbnail_factory, pixbuf, uri, mtime);
+        }
+      else
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to generate thumbnail");
+          gnome_desktop_thumbnail_factory_create_failed_thumbnail (thumbnail_factory, uri, mtime);
+        }
+     }
+   return pixbuf;
+}
+
+static GIcon *
+icon_for_mimetype (const char *mimetype)
 {
-  if (G_UNLIKELY (__cache_singleton == NULL))
-    __cache_singleton = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
+  char *content_type;
+  GIcon *icon;
+
+  content_type = g_content_type_from_mime_type (mimetype);
+  if (!content_type)
+    return NULL;
 
-  return __cache_singleton;
+  icon = g_content_type_get_icon (content_type);
+  g_free (content_type);
+  return icon;
 }
 
-#if 0
 static void
-on_texure_finalized (gpointer data,
-                     GObject *where_the_object_was)
+load_pixbuf_thread (GSimpleAsyncResult *result,
+                    GObject *object,
+                    GCancellable *cancellable)
 {
-  FinalizedClosure *closure = (FinalizedClosure *) data;
-  StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(closure->cache);
+  GdkPixbuf *pixbuf;
+  AsyncIconLookupData *data;
+  GError *error = NULL;
+
+  data = g_object_get_data (G_OBJECT (result), "load_pixbuf_async");
+  g_assert (data != NULL);
 
-  g_hash_table_remove (priv->cache, closure->path);
+  if (data->thumbnail)
+    {
+      const char *uri;
+      const char *mimetype;
 
-  g_free(closure->path);
-  g_free(closure);
+      if (data->recent_info)
+        {
+          uri = gtk_recent_info_get_uri (data->recent_info);
+          mimetype = gtk_recent_info_get_mime_type (data->recent_info);
+        }
+      else
+        {
+          uri = data->uri;
+          mimetype = data->mimetype;
+        }
+      pixbuf = impl_load_thumbnail (data->cache, uri, mimetype, data->width, &error);
+    }
+  else if (data->uri)
+    pixbuf = impl_load_pixbuf_file (data->uri, data->width, data->height, &error);
+  else if (data->icon)
+    pixbuf = impl_load_pixbuf_gicon (data->icon, data->icon_info, data->width, &error);
+  else
+    g_assert_not_reached ();
+
+  if (error != NULL)
+    {
+      g_simple_async_result_set_from_error (result, error);
+      return;
+    }
+
+  if (pixbuf)
+    g_simple_async_result_set_op_res_gpointer (result, g_object_ref (pixbuf),
+                                               g_object_unref);
 }
-#endif
 
 /**
- * st_texture_cache_get_size:
- * @self: A #StTextureCache
+ * load_icon_pixbuf_async:
  *
- * Returns the number of items in the texture cache
- *
- * Returns: the current size of the cache
+ * Asynchronously load the #GdkPixbuf associated with a #GIcon.  Currently
+ * the #GtkIconInfo must have already been provided.
  */
-gint
-st_texture_cache_get_size (StTextureCache *self)
+static void
+load_icon_pixbuf_async (StTextureCache       *cache,
+                        GIcon                *icon,
+                        GtkIconInfo          *icon_info,
+                        gint                  size,
+                        GCancellable         *cancellable,
+                        GAsyncReadyCallback   callback,
+                        gpointer              user_data)
 {
-  StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+  GSimpleAsyncResult *result;
+  AsyncIconLookupData *data;
+
+  data = g_new0 (AsyncIconLookupData, 1);
+  data->cache = cache;
+  data->icon = g_object_ref (icon);
+  data->icon_info = gtk_icon_info_copy (icon_info);
+  data->width = data->height = size;
+  data->user_data = user_data;
+
+  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_icon_pixbuf_async);
+
+  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
+  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
+
+  g_object_unref (result);
+}
+
+static void
+load_uri_pixbuf_async (StTextureCache     *cache,
+                       const char         *uri,
+                       guint               width,
+                       guint               height,
+                       GCancellable       *cancellable,
+                       GAsyncReadyCallback callback,
+                       gpointer            user_data)
+{
+  GSimpleAsyncResult *result;
+  AsyncIconLookupData *data;
+
+  data = g_new0 (AsyncIconLookupData, 1);
+  data->cache = cache;
+  data->uri = g_strdup (uri);
+  data->width = width;
+  data->height = height;
+  data->user_data = user_data;
+
+  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_uri_pixbuf_async);
+
+  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
+  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
+
+  g_object_unref (result);
+}
+
+static void
+load_thumbnail_async (StTextureCache     *cache,
+                      const char         *uri,
+                      const char         *mimetype,
+                      guint               size,
+                      GCancellable       *cancellable,
+                      GAsyncReadyCallback callback,
+                      gpointer            user_data)
+{
+  GSimpleAsyncResult *result;
+  AsyncIconLookupData *data;
+
+  data = g_new0 (AsyncIconLookupData, 1);
+  data->cache = cache;
+  data->uri = g_strdup (uri);
+  data->mimetype = g_strdup (mimetype);
+  data->thumbnail = TRUE;
+  data->width = size;
+  data->height = size;
+  data->user_data = user_data;
+
+  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_thumbnail_async);
+
+  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
+  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
+
+  g_object_unref (result);
+}
+
+static void
+load_recent_thumbnail_async (StTextureCache     *cache,
+                             GtkRecentInfo      *info,
+                             guint               size,
+                             GCancellable       *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer            user_data)
+{
+  GSimpleAsyncResult *result;
+  AsyncIconLookupData *data;
+
+  data = g_new0 (AsyncIconLookupData, 1);
+  data->cache = cache;
+  data->thumbnail = TRUE;
+  data->recent_info = gtk_recent_info_ref (info);
+  data->width = size;
+  data->height = size;
+  data->user_data = user_data;
+
+  result = g_simple_async_result_new (G_OBJECT (cache), callback, user_data, load_recent_thumbnail_async);
+
+  g_object_set_data_full (G_OBJECT (result), "load_pixbuf_async", data, icon_lookup_data_destroy);
+  g_simple_async_result_run_in_thread (result, load_pixbuf_thread, G_PRIORITY_DEFAULT, cancellable);
+
+  g_object_unref (result);
+}
+
+static GdkPixbuf *
+load_pixbuf_async_finish (StTextureCache *cache, GAsyncResult *result, GError **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+  return g_simple_async_result_get_op_res_gpointer (simple);
+}
+
+typedef struct {
+  StTextureCachePolicy policy;
+  char *uri;
+  gboolean thumbnail;
+  char *mimetype;
+  GtkRecentInfo *recent_info;
+  char *checksum;
+  GIcon *icon;
+  GtkIconInfo *icon_info;
+  guint width;
+  guint height;
+  GSList *textures;
+} AsyncTextureLoadData;
+
+static CoglHandle
+pixbuf_to_cogl_handle (GdkPixbuf *pixbuf)
+{
+  return cogl_texture_new_from_data (gdk_pixbuf_get_width (pixbuf),
+                                     gdk_pixbuf_get_height (pixbuf),
+                                     COGL_TEXTURE_NONE,
+                                     gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
+                                     COGL_PIXEL_FORMAT_ANY,
+                                     gdk_pixbuf_get_rowstride (pixbuf),
+                                     gdk_pixbuf_get_pixels (pixbuf));
+}
+
+static GdkPixbuf *
+load_pixbuf_fallback(AsyncTextureLoadData *data)
+{
+  GdkPixbuf *pixbuf = NULL;
+
+  if (data->thumbnail)
+    {
+
+      GtkIconTheme *theme = gtk_icon_theme_get_default ();
+
+      if (data->recent_info)
+          pixbuf = gtk_recent_info_get_icon (data->recent_info, data->width);
+      else
+        {
+          GIcon *icon = icon_for_mimetype (data->mimetype);
+          if (icon != NULL)
+            {
+              GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon (theme,
+                                                                       icon,
+                                                                       data->width,
+                                                                       GTK_ICON_LOOKUP_USE_BUILTIN);
+              g_object_unref (icon);
+              if (icon_info != NULL)
+                pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
+            }
+        }
+
+      if (pixbuf == NULL)
+        pixbuf = gtk_icon_theme_load_icon (theme,
+                                           "gtk-file",
+                                           data->width,
+                                           GTK_ICON_LOOKUP_USE_BUILTIN,
+                                           NULL);
+    }
+  /* Maybe we could need a fallback for outher image types? */
+
+  return pixbuf;
+}
+
+static void
+on_pixbuf_loaded (GObject      *source,
+                  GAsyncResult *result,
+                  gpointer      user_data)
+{
+  GSList *iter;
+  StTextureCache *cache;
+  AsyncTextureLoadData *data;
+  GdkPixbuf *pixbuf;
+  GError *error = NULL;
+  CoglHandle texdata = NULL;
+  CacheKey key;
+
+  data = user_data;
+  cache = ST_TEXTURE_CACHE (source);
+
+  memset (&key, 0, sizeof(key));
+  key.policy = data->policy;
+  if (data->icon)
+    key.icon = data->icon;
+  else if (data->recent_info && data->thumbnail)
+    key.thumbnail_uri = (char*)gtk_recent_info_get_uri (data->recent_info);
+  else if (data->thumbnail)
+    key.thumbnail_uri = (char*)data->uri;
+  else if (data->uri)
+    key.uri = data->uri;
+  key.size = data->width;
+
+  g_hash_table_remove (cache->priv->outstanding_requests, &key);
+
+  pixbuf = load_pixbuf_async_finish (cache, result, &error);
+  if (pixbuf == NULL)
+    pixbuf = load_pixbuf_fallback(data);
+  if (pixbuf == NULL)
+    goto out;
+
+  texdata = pixbuf_to_cogl_handle (pixbuf);
+
+  g_object_unref (pixbuf);
+
+  if (data->policy != ST_TEXTURE_CACHE_POLICY_NONE)
+    {
+      gpointer orig_key, value;
+
+      if (!g_hash_table_lookup_extended (cache->priv->keyed_cache, &key,
+                                         &orig_key, &value))
+        {
+          cogl_handle_ref (texdata);
+          g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key),
+                               texdata);
+        }
+    }
+
+  for (iter = data->textures; iter; iter = iter->next)
+    {
+      ClutterTexture *texture = iter->data;
+      set_texture_cogl_texture (texture, texdata);
+    }
+
+out:
+  if (texdata)
+    cogl_handle_unref (texdata);
+  if (data->icon)
+    {
+      gtk_icon_info_free (data->icon_info);
+      g_object_unref (data->icon);
+    }
+  else if (data->uri)
+    g_free (data->uri);
+
+  if (data->recent_info)
+    gtk_recent_info_unref (data->recent_info);
+  if (data->mimetype)
+    g_free (data->mimetype);
 
-  return g_hash_table_size (priv->cache);
+  /* Alternatively we could weakref and just do nothing if the texture
+     is destroyed */
+  for (iter = data->textures; iter; iter = iter->next)
+    {
+      ClutterTexture *texture = iter->data;
+      g_object_unref (texture);
+    }
+
+  g_clear_error (&error);
+  g_free (data);
 }
 
+typedef struct {
+  StTextureCache *cache;
+  ClutterTexture *texture;
+  GObject *source;
+  guint notify_signal_id;
+  gboolean weakref_active;
+} StTextureCachePropertyBind;
+
 static void
-add_texture_to_cache (StTextureCache     *self,
-                      const gchar        *path,
-                      StTextureCacheItem *item)
+st_texture_cache_reset_texture (StTextureCachePropertyBind *bind,
+                                const char                 *propname)
 {
-  /*  FinalizedClosure        *closure; */
-  StTextureCachePrivate *priv = TEXTURE_CACHE_PRIVATE(self);
+  GdkPixbuf *pixbuf;
+  CoglHandle texdata;
+
+  g_object_get (bind->source, propname, &pixbuf, NULL);
 
-  g_hash_table_insert (priv->cache, g_strdup (path), item);
+  g_return_if_fail (pixbuf == NULL || GDK_IS_PIXBUF (pixbuf));
 
-#if 0
-  /* Make sure we can remove from hash */
-  closure = g_new0 (FinalizedClosure, 1);
-  closure->path = g_strdup (path);
-  closure->cache = self;
+  if (pixbuf != NULL)
+    {
+      texdata = pixbuf_to_cogl_handle (pixbuf);
+      g_object_unref (pixbuf);
 
-  g_object_weak_ref (G_OBJECT (res), on_texure_finalized, closure);
-#endif
+      clutter_texture_set_cogl_texture (bind->texture, texdata);
+      cogl_handle_unref (texdata);
+
+      clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 255);
+    }
+  else
+    clutter_actor_set_opacity (CLUTTER_ACTOR (bind->texture), 0);
 }
 
-/* NOTE: you should unref the returned texture when not needed */
+static void
+st_texture_cache_on_pixbuf_notify (GObject           *object,
+                                   GParamSpec        *paramspec,
+                                   gpointer           data)
+{
+  StTextureCachePropertyBind *bind = data;
+  st_texture_cache_reset_texture (bind, paramspec->name);
+}
+
+static void
+st_texture_cache_bind_weak_notify (gpointer     data,
+                                   GObject     *source_location)
+{
+  StTextureCachePropertyBind *bind = data;
+  bind->weakref_active = FALSE;
+  g_signal_handler_disconnect (bind->source, bind->notify_signal_id);
+}
+
+static void
+st_texture_cache_free_bind (gpointer data)
+{
+  StTextureCachePropertyBind *bind = data;
+  if (bind->weakref_active)
+    g_object_weak_unref (G_OBJECT(bind->texture), st_texture_cache_bind_weak_notify, bind);
+  g_free (bind);
+}
 
 /**
- * st_texture_cache_get_texture:
- * @self: A #StTextureCache
- * @path: A path to a image file
+ * st_texture_cache_bind_pixbuf_property:
+ * @cache:
+ * @object: A #GObject with a property @property_name of type #GdkPixbuf
+ * @property_name: Name of a property
+ *
+ * Create a #ClutterTexture which tracks the #GdkPixbuf value of a GObject property
+ * named by @property_name.  Unlike other methods in StTextureCache, the underlying
+ * CoglHandle is not shared by default with other invocations to this method.
  *
- * Create a new ClutterTexture with the specified image. Adds the image to the
- * cache if the image had not been previously loaded. Subsequent calls with
- * the same image path will return a new ClutterTexture with the previously
- * loaded image.
+ * If the source object is destroyed, the texture will continue to show the last
+ * value of the property.
  *
- * Returns: (transfer none): a newly created ClutterTexture
+ * Return value: (transfer none): A new #ClutterActor
  */
-ClutterTexture*
-st_texture_cache_get_texture (StTextureCache *self,
-                              const gchar    *path)
+ClutterActor *
+st_texture_cache_bind_pixbuf_property (StTextureCache    *cache,
+                                       GObject           *object,
+                                       const char        *property_name)
 {
-  ClutterActor *texture;
-  CoglHandle *handle;
-  StTextureCachePrivate *priv;
-  StTextureCacheItem *item;
+  ClutterTexture *texture;
+  gchar *notify_key;
+  StTextureCachePropertyBind *bind;
 
-  g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
+  texture = CLUTTER_TEXTURE (clutter_texture_new ());
 
+  bind = g_new0 (StTextureCachePropertyBind, 1);
+  bind->cache = cache;
+  bind->texture = texture;
+  bind->source = object;
+  g_object_weak_ref (G_OBJECT (texture), st_texture_cache_bind_weak_notify, bind);
+  bind->weakref_active = TRUE;
 
-  priv = TEXTURE_CACHE_PRIVATE (self);
+  st_texture_cache_reset_texture (bind, property_name);
 
-  item = g_hash_table_lookup (priv->cache, path);
+  notify_key = g_strdup_printf ("notify::%s", property_name);
+  bind->notify_signal_id = g_signal_connect_data (object, notify_key, G_CALLBACK(st_texture_cache_on_pixbuf_notify),
+                                                  bind, (GClosureNotify)st_texture_cache_free_bind, 0);
+  g_free (notify_key);
 
-  if (item && item->posX != -1)
+  return CLUTTER_ACTOR(texture);
+}
+
+/**
+ * create_texture_and_ensure_request:
+ * @cache:
+ * @key: A filled in #CacheKey
+ * @request: (out): If no request is outstanding, one will be created and returned here
+ * @texture: (out): A new texture, also added to the request
+ *
+ * Check for any outstanding load for the data represented by @key.  If there
+ * is already a request pending, append it to that request to avoid loading
+ * the data multiple times.
+ *
+ * Returns: %TRUE iff there is already a request pending
+ */
+static gboolean
+create_texture_and_ensure_request (StTextureCache        *cache,
+                                   CacheKey              *key,
+                                   AsyncTextureLoadData **request,
+                                   ClutterActor         **texture)
+{
+  CoglHandle texdata;
+  AsyncTextureLoadData *pending;
+  gboolean had_pending;
+
+  *texture = (ClutterActor *) create_default_texture (cache);
+  clutter_actor_set_size (*texture, key->size, key->size);
+
+  texdata = g_hash_table_lookup (cache->priv->keyed_cache, key);
+
+  if (texdata != NULL)
     {
-      GError *err = NULL;
-      /*
-       * We have a cache hit, but it's for a partial texture. The only
-       * sane option is to read it from disk and just don't cache it
-       * at all.
-       */
-      return CLUTTER_TEXTURE(clutter_texture_new_from_file(path, &err));
+      /* We had this cached already, just set the texture and we're done. */
+      set_texture_cogl_texture (CLUTTER_TEXTURE (*texture), texdata);
+      return TRUE;
     }
-  if (!item)
+
+  pending = g_hash_table_lookup (cache->priv->outstanding_requests, key);
+  had_pending = pending != NULL;
+
+  if (pending == NULL)
     {
-      GError *err = NULL;
+      /* Not cached and no pending request, create it */
+      *request = g_new0 (AsyncTextureLoadData, 1);
+      g_hash_table_insert (cache->priv->outstanding_requests, cache_key_dup (key), *request);
+    }
+  else
+   *request = pending;
 
-      item = st_texture_cache_item_new ();
-      item->posX = -1;
-      item->posY = -1;
-      item->ptr = clutter_texture_new_from_file (path, &err);
-      clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
-                                     &item->width, &item->height);
+  /* Regardless of whether there was a pending request, prepend our texture here. */
+  (*request)->textures = g_slist_prepend ((*request)->textures, g_object_ref (*texture));
 
-      if (!item->ptr)
-        {
-          if (err)
-            {
-              g_warning ("Error loading image: %s", err->message);
-              g_error_free (err);
-            }
+  return had_pending;
+}
 
-          return NULL;
-        }
+/**
+ * st_texture_cache_load_gicon:
+ *
+ * This method returns a new #ClutterClone for a given #GIcon.  If the
+ * icon isn't loaded already, the texture will be filled asynchronously.
+ *
+ * Return Value: (transfer none): A new #ClutterActor for the icon
+ */
+ClutterActor *
+st_texture_cache_load_gicon (StTextureCache    *cache,
+                             GIcon             *icon,
+                             gint               size)
+{
+  AsyncTextureLoadData *request;
+  ClutterActor *texture;
+  CacheKey key;
+  GtkIconTheme *theme;
+  GtkIconInfo *info;
+
+  memset (&key, 0, sizeof(key));
+  key.icon = icon;
+  key.size = size;
 
-      add_texture_to_cache (self, path, item);
+  if (create_texture_and_ensure_request (cache, &key, &request, &texture))
+    return texture;
+
+  /* Do theme lookups in the main thread to avoid thread-unsafety */
+  theme = gtk_icon_theme_get_default ();
+
+  info = gtk_icon_theme_lookup_by_gicon (theme, icon, size, GTK_ICON_LOOKUP_USE_BUILTIN);
+  if (info != NULL)
+    {
+      /* hardcoded here for now; we should actually blow this away on
+       * icon theme changes probably */
+      request->policy = ST_TEXTURE_CACHE_POLICY_FOREVER;
+      request->icon = g_object_ref (icon);
+      request->icon_info = info;
+      request->width = request->height = size;
+
+      load_icon_pixbuf_async (cache, icon, info, size, NULL, on_pixbuf_loaded, request);
+    }
+  else
+    {
+      /* Blah; we failed to find the icon, but we've added our texture to the outstanding
+       * requests.  In that case, just undo what create_texture_lookup_status did.
+       */
+       g_slist_foreach (request->textures, (GFunc) g_object_unref, NULL);
+       g_slist_free (request->textures);
+       g_free (request);
+       g_hash_table_remove (cache->priv->outstanding_requests, &key);
     }
 
-  texture = clutter_texture_new ();
-  handle = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (item->ptr));
-  clutter_texture_set_cogl_texture ((ClutterTexture*) texture, handle);
+  return CLUTTER_ACTOR (texture);
+}
+
+/**
+ * st_texture_cache_load_icon_name:
+ * @cache: The texture cache instance
+ * @name: Name of a themed icon
+ * @size: Size of themed
+ *
+ * Load a themed icon into a texture.
+ *
+ * Return Value: (transfer none): A new #ClutterTexture for the icon
+ */
+ClutterActor *
+st_texture_cache_load_icon_name (StTextureCache    *cache,
+                                 const char        *name,
+                                 gint               size)
+{
+  ClutterActor *texture;
+  GIcon *themed;
+
+  themed = g_themed_icon_new (name);
+  texture = st_texture_cache_load_gicon (cache, themed, size);
+  g_object_unref (themed);
 
-  return (ClutterTexture*) texture;
+  return CLUTTER_ACTOR (texture);
 }
 
+/**
+ * st_texture_cache_load_uri_async:
+ *
+ * @cache: The texture cache instance
+ * @uri: uri of the image file from which to create a pixbuf
+ * @available_width: available width for the image, can be -1 if not limited
+ * @available_height: available height for the image, can be -1 if not limited
+ *
+ * Asynchronously load an image.   Initially, the returned texture will have a natural
+ * size of zero.  At some later point, either the image will be loaded successfully
+ * and at that point size will be negotiated, or upon an error, no image will be set.
+ *
+ * Return value: (transfer none): A new #ClutterActor with no image loaded initially.
+ */
+ClutterActor *
+st_texture_cache_load_uri_async (StTextureCache *cache,
+                                 const gchar    *uri,
+                                 int             available_width,
+                                 int             available_height)
+{
+  ClutterTexture *texture;
+  AsyncTextureLoadData *data;
+
+  texture = create_default_texture (cache);
+
+  data = g_new0 (AsyncTextureLoadData, 1);
+  data->policy = ST_TEXTURE_CACHE_POLICY_NONE;
+  data->uri = g_strdup (uri);
+  data->width = available_width;
+  data->height = available_height;
+  data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
+  load_uri_pixbuf_async (cache, uri, available_width, available_height, NULL, on_pixbuf_loaded, data);
+
+  return CLUTTER_ACTOR (texture);
+}
 
 /**
- * st_texture_cache_get_actor:
- * @self: A #StTextureCache
- * @path: A path to a image file
+ * st_texture_cache_load_uri_sync:
  *
- * Create a new ClutterSubTexture with the specified image. Adds the image to the
- * cache if the image had not been previously loaded. Subsequent calls with
- * the same image path will return a new ClutterTexture with the previously
- * loaded image.
+ * @cache: The texture cache instance
+ * @policy: Requested lifecycle of cached data
+ * @uri: uri of the image file from which to create a pixbuf
+ * @available_width: available width for the image, can be -1 if not limited
+ * @available_height: available height for the image, can be -1 if not limited
+ * @error: Return location for error
  *
- * Use this function if all you need is an actor for drawing.
+ * Synchronously load an image from a uri.  The image is scaled down to fit the
+ * available width and height imensions, but the image is never scaled up beyond
+ * its actual size. The pixbuf is rotated according to the associated orientation
+ * setting.
  *
- * Returns: (transfer none): a newly created ClutterTexture
+ * Return value: (transfer none): A new #ClutterActor with the image file loaded if it was
+ *               generated succesfully, %NULL otherwise
  */
-ClutterActor*
-st_texture_cache_get_actor (StTextureCache *self,
-                            const gchar    *path)
+ClutterActor *
+st_texture_cache_load_uri_sync (StTextureCache *cache,
+                                StTextureCachePolicy policy,
+                                const gchar       *uri,
+                                int                available_width,
+                                int                available_height,
+                                GError            **error)
 {
-  StTextureCachePrivate *priv;
-  StTextureCacheItem *item;
-  GError *err = NULL;
+  ClutterTexture *texture;
+  CoglHandle texdata;
+  GdkPixbuf *pixbuf;
+  CacheKey key;
+
+  texture = create_default_texture (cache);
+
+  memset (&key, 0, sizeof (CacheKey));
+  key.policy = policy;
+  key.uri = (char*)uri;
+  key.size = available_width;
+  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
+
+  if (texdata == NULL)
+    {
+      pixbuf = impl_load_pixbuf_file (uri, available_width, available_height, error);
+      if (!pixbuf)
+        {
+          g_object_unref (texture);
+          return NULL;
+        }
+
+      texdata = pixbuf_to_cogl_handle (pixbuf);
+      g_object_unref (pixbuf);
 
-  g_return_val_if_fail (ST_IS_TEXTURE_CACHE (self), NULL);
-  g_return_val_if_fail (path != NULL, NULL);
+      set_texture_cogl_texture (texture, texdata);
+
+      if (policy == ST_TEXTURE_CACHE_POLICY_FOREVER)
+        {
+          g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
+        }
+      else
+        cogl_handle_unref (texdata);
+    }
+  else
+    set_texture_cogl_texture (texture, texdata);
 
-  priv = TEXTURE_CACHE_PRIVATE (self);
+  return CLUTTER_ACTOR (texture);
+}
 
+/**
+ * st_texture_cache_load_file_simple:
+ * @cache: A #StTextureCache
+ * @file_path: Filesystem path
+ *
+ * Synchronously load an image into a texture.  The texture will be cached
+ * indefinitely.  On error, this function returns an empty texture and prints a warning.
+ *
+ * Returns: (transfer none): A new #ClutterTexture
+ */
+ClutterActor *
+st_texture_cache_load_file_simple (StTextureCache *cache,
+                                   const gchar    *file_path)
+{
+  GFile *file;
+  char *uri;
+  ClutterActor *texture;
+  GError *error = NULL;
 
-  item = g_hash_table_lookup (priv->cache, path);
+  file = g_file_new_for_path (file_path);
+  uri = g_file_get_uri (file);
 
-  if (item)
+  texture = st_texture_cache_load_uri_sync (cache, ST_TEXTURE_CACHE_POLICY_FOREVER,
+                                            uri, -1, -1, &error);
+  if (texture == NULL)
     {
-      int posX = item->posX;
-      int posY = item->posY;
-      if (posX == -1)
-        posX = 0;
-      if (posY == -1)
-        posY = 0;
-      return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), posX, posY,
-                                item->width, item->height);
+      g_warning ("Failed to load %s: %s", file_path, error->message);
+      g_clear_error (&error);
+      texture = clutter_texture_new ();
     }
+  return texture;
+}
+
+/**
+ * st_texture_cache_load_from_data:
+ * @cache: The texture cache instance
+ * @data: Image data in PNG, GIF, etc format
+ * @len: length of @data
+ * @size: Size in pixels to use for the resulting texture
+ * @error: Return location for error
+ *
+ * Synchronously creates an image from @data. The image is scaled down
+ * to fit the available width and height dimensions, but the image is
+ * never scaled up beyond its actual size. The pixbuf is rotated
+ * according to the associated orientation setting.
+ *
+ * Return value: (transfer none): A new #ClutterActor with the image data loaded if it was
+ *               generated succesfully, %NULL otherwise
+ */
+ClutterActor *
+st_texture_cache_load_from_data (StTextureCache    *cache,
+                                 const guchar      *data,
+                                 gsize              len,
+                                 int                size,
+                                 GError           **error)
+{
+  ClutterTexture *texture;
+  CoglHandle texdata;
+  GdkPixbuf *pixbuf;
+  CacheKey key;
+  gchar *checksum;
+
+  texture = create_default_texture (cache);
+  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
+
+  checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len);
 
-  item = st_texture_cache_item_new ();
-  item->posX = -1;
-  item->posY = -1;
-  item->ptr = clutter_texture_new_from_file (path, &err);
-  clutter_texture_get_base_size (CLUTTER_TEXTURE (item->ptr),
-                                 &item->width, &item->height);
+  memset (&key, 0, sizeof(key));
+  key.size = size;
+  key.checksum = checksum;
 
-  if (!item->ptr)
+  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
+  if (texdata == NULL)
     {
-      if (err)
+      pixbuf = impl_load_pixbuf_data (data, len, size, size, error);
+      if (!pixbuf)
         {
-          g_warning ("Error loading image: %s", err->message);
-          g_error_free (err);
+          g_object_unref (texture);
+          return NULL;
         }
 
-      return NULL;
+      texdata = pixbuf_to_cogl_handle (pixbuf);
+      g_object_unref (pixbuf);
+
+      set_texture_cogl_texture (texture, texdata);
+
+      g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
     }
 
-  add_texture_to_cache (self, path, item);
+  g_free (key.checksum);
 
-  return st_subtexture_new (CLUTTER_TEXTURE (item->ptr), 0, 0, item->width,
-                            item->height);
+  set_texture_cogl_texture (texture, texdata);
+  return CLUTTER_ACTOR (texture);
 }
 
-void
-st_texture_cache_load_cache (StTextureCache *self,
-                             const gchar    *filename)
+/**
+ * st_texture_cache_load_from_raw:
+ * @cache: a #StTextureCache
+ * @data: raw pixel data
+ * @len: the length of @data
+ * @has_alpha: whether @data includes an alpha channel
+ * @width: width in pixels of @data
+ * @height: width in pixels of @data
+ * @rowstride: rowstride of @data
+ * @size: size of icon to return
+ *
+ * Creates (or retrieves from cache) an icon based on raw pixel data.
+ *
+ * Return value: (transfer none): a new #ClutterActor displaying a
+ * pixbuf created from @data and the other parameters.
+ **/
+ClutterActor *
+st_texture_cache_load_from_raw (StTextureCache    *cache,
+                                const guchar      *data,
+                                gsize              len,
+                                gboolean           has_alpha,
+                                int                width,
+                                int                height,
+                                int                rowstride,
+                                int                size,
+                                GError           **error)
 {
-  FILE *file;
-  StTextureCacheItem *element, head;
-  int ret;
-  ClutterActor *actor;
-  GError *error = NULL;
-  StTextureCachePrivate *priv;
+  ClutterTexture *texture;
+  CoglHandle texdata;
+  CacheKey key;
+  gchar *checksum;
 
-  g_return_if_fail (ST_IS_TEXTURE_CACHE (self));
-  g_return_if_fail (filename != NULL);
+  texture = create_default_texture (cache);
+  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
 
-  priv = TEXTURE_CACHE_PRIVATE (self);
+  /* In theory, two images of different size could have the same
+   * pixel data. We ignore that theory.
+   */
+  checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA1, data, len);
 
-  file = fopen(filename, "rm");
-  if (!file)
-    return;
+  memset (&key, 0, sizeof(key));
+  key.size = size;
+  key.checksum = checksum;
 
-  ret = fread (&head, sizeof(StTextureCacheItem), 1, file);
-  if (ret < 0)
+  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
+  if (texdata == NULL)
     {
-      fclose (file);
-      return;
+      texdata = cogl_texture_new_from_data (width, height, COGL_TEXTURE_NONE,
+                                            has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888,
+                                            COGL_PIXEL_FORMAT_ANY,
+                                            rowstride, data);
+      g_hash_table_insert (cache->priv->keyed_cache, cache_key_dup (&key), texdata);
     }
 
-  /* check if we already if this texture in the cache */
-  if (g_hash_table_lookup (priv->cache, head.filename))
+  g_free (key.checksum);
+
+  set_texture_cogl_texture (texture, texdata);
+  return CLUTTER_ACTOR (texture);
+}
+
+/**
+ * st_texture_cache_load_thumbnail:
+ * @cache:
+ * @size: Size in pixels to use for thumbnail
+ * @uri: Source URI
+ * @mimetype: Source mime type
+ *
+ * Asynchronously load a thumbnail image of a URI into a texture.  The
+ * returned texture object will be a new instance; however, its texture data
+ * may be shared with other objects.  This implies the texture data is cached.
+ *
+ * The current caching policy is permanent; to uncache, you must explicitly
+ * call st_texture_cache_unref_thumbnail().
+ *
+ * Returns: (transfer none): A new #ClutterActor
+ */
+ClutterActor *
+st_texture_cache_load_thumbnail (StTextureCache    *cache,
+                                 int                size,
+                                 const char        *uri,
+                                 const char        *mimetype)
+{
+  ClutterTexture *texture;
+  AsyncTextureLoadData *data;
+  CacheKey key;
+  CoglHandle texdata;
+
+  /* Don't attempt to load thumbnails for non-local URIs */
+  if (!g_str_has_prefix (uri, "file://"))
     {
-      /* skip it, we're done */
-      fclose (file);
-      return;
+      GIcon *icon = icon_for_mimetype (mimetype);
+      return st_texture_cache_load_gicon (cache, icon, size);
     }
 
-  actor = clutter_texture_new_from_file (head.filename, &error);
+  texture = create_default_texture (cache);
+  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
+
+  memset (&key, 0, sizeof(key));
+  key.size = size;
+  key.thumbnail_uri = (char*)uri;
 
-  if (error)
+  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
+  if (!texdata)
     {
-      g_critical (G_STRLOC ": Error opening cache image file: %s",
-                  error->message);
-      g_clear_error (&error);
-      fclose (file);
-      return;
+      data = g_new0 (AsyncTextureLoadData, 1);
+      data->policy = ST_TEXTURE_CACHE_POLICY_FOREVER;
+      data->uri = g_strdup (uri);
+      data->mimetype = g_strdup (mimetype);
+      data->thumbnail = TRUE;
+      data->width = size;
+      data->height = size;
+      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
+      load_thumbnail_async (cache, uri, mimetype, size, NULL, on_pixbuf_loaded, data);
+    }
+  else
+    {
+      set_texture_cogl_texture (texture, texdata);
     }
 
-  element = st_texture_cache_item_new ();
-  element->posX = -1;
-  element->posY = -1;
-  element->ptr = actor;
-  strncpy (element->filename, head.filename, 256);
-  clutter_texture_get_base_size (CLUTTER_TEXTURE (element->ptr),
-                                 &element->width, &element->height);
-  g_hash_table_insert (priv->cache, element->filename, element);
+  return CLUTTER_ACTOR (texture);
+}
+
+static GIcon *
+icon_for_recent (GtkRecentInfo *info)
+{
+  const char *mimetype;
 
-  while (!feof (file))
+  mimetype = gtk_recent_info_get_mime_type (info);
+  if (!mimetype)
     {
-      element = st_texture_cache_item_new ();
-      ret = fread (element, sizeof (StTextureCacheItem), 1, file);
-      if (ret < 1)
-        {
-          /* end of file */
-          st_texture_cache_item_free (element);
-          break;
-        }
+      return g_themed_icon_new (GTK_STOCK_FILE);
+    }
 
-      element->ptr = actor;
+  return icon_for_mimetype (mimetype);
+}
 
-      if (g_hash_table_lookup (priv->cache, element->filename))
-        {
-          /* file is already in the cache.... */
-          st_texture_cache_item_free (element);
-        } else {
-          g_hash_table_insert (priv->cache, element->filename, element);
-        }
+/**
+ * st_texture_cache_load_recent_thumbnail:
+ * @cache:
+ * @size: Size in pixels to use for thumbnail
+ * @info: Recent item info
+ *
+ * Asynchronously load a thumbnail image of a #GtkRecentInfo into a texture.  The
+ * returned texture object will be a new instance; however, its texture data
+ * may be shared with other objects.  This implies the texture data is cached.
+ *
+ * The current caching policy is permanent; to uncache, you must explicitly
+ * call st_texture_cache_unref_recent_thumbnail().
+ *
+ * Returns: (transfer none): A new #ClutterActor
+ */
+ClutterActor *
+st_texture_cache_load_recent_thumbnail (StTextureCache    *cache,
+                                        int                size,
+                                        GtkRecentInfo     *info)
+{
+  ClutterTexture *texture;
+  AsyncTextureLoadData *data;
+  CacheKey key;
+  CoglHandle texdata;
+  const char *uri;
+
+  uri = gtk_recent_info_get_uri (info);
+
+  /* Don't attempt to load thumbnails for non-local URIs */
+  if (!g_str_has_prefix (uri, "file://"))
+    {
+      GIcon *icon = icon_for_recent (info);
+      return st_texture_cache_load_gicon (cache, icon, size);
+    }
+
+  texture = CLUTTER_TEXTURE (clutter_texture_new ());
+  clutter_actor_set_size (CLUTTER_ACTOR (texture), size, size);
+
+  memset (&key, 0, sizeof(key));
+  key.size = size;
+  key.thumbnail_uri = (char*)gtk_recent_info_get_uri (info);
+
+  texdata = g_hash_table_lookup (cache->priv->keyed_cache, &key);
+  if (!texdata)
+    {
+      data = g_new0 (AsyncTextureLoadData, 1);
+      data->policy = ST_TEXTURE_CACHE_POLICY_FOREVER;
+      data->thumbnail = TRUE;
+      data->recent_info = gtk_recent_info_ref (info);
+      data->width = size;
+      data->height = size;
+      data->textures = g_slist_prepend (data->textures, g_object_ref (texture));
+      load_recent_thumbnail_async (cache, info, size, NULL, on_pixbuf_loaded, data);
+    }
+  else
+    {
+      set_texture_cogl_texture (texture, texdata);
+    }
+
+  return CLUTTER_ACTOR (texture);
+}
+
+/**
+ * st_texture_cache_evict_thumbnail:
+ * @cache:
+ * @uri: Source URI
+ *
+ * Removes all references added by st_texture_cache_load_thumbnail() function
+ * created for the given URI.
+ */
+void
+st_texture_cache_evict_thumbnail (StTextureCache    *cache,
+                                  const char        *uri)
+{
+  GHashTableIter iter;
+  gpointer key, value;
+
+  g_hash_table_iter_init (&iter, cache->priv->keyed_cache);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      CacheKey *cachekey = key;
+
+      if (cachekey->thumbnail_uri == NULL || strcmp (cachekey->thumbnail_uri, uri) != 0)
+        continue;
+
+      g_hash_table_iter_remove (&iter);
     }
 }
+
+/**
+ * st_texture_cache_evict_recent_thumbnail:
+ * @cache:
+ * @info: A recent info
+ *
+ * Removes all references added by st_texture_cache_load_recent_thumbnail() function
+ * for the URI associated with the given @info.
+ */
+void
+st_texture_cache_evict_recent_thumbnail (StTextureCache *cache,
+                                         GtkRecentInfo  *info)
+{
+  st_texture_cache_evict_thumbnail (cache, gtk_recent_info_get_uri (info));
+}
+
+static size_t
+pixbuf_byte_size (GdkPixbuf *pixbuf)
+{
+  /* This bit translated from gtk+/gdk-pixbuf/gdk-pixbuf.c:gdk_pixbuf_copy.  The comment
+   * there was:
+   *
+   * Calculate a semi-exact size.  Here we copy with full rowstrides;
+   * maybe we should copy each row individually with the minimum
+   * rowstride?
+   */
+  return (gdk_pixbuf_get_height (pixbuf) - 1) * gdk_pixbuf_get_rowstride (pixbuf) +
+    + gdk_pixbuf_get_width (pixbuf) * ((gdk_pixbuf_get_n_channels (pixbuf)* gdk_pixbuf_get_bits_per_sample (pixbuf) + 7) / 8);
+}
+
+/**
+ * st_texture_cache_pixbuf_equal:
+ *
+ * Returns: %TRUE iff the given pixbufs are bytewise-equal
+ */
+gboolean
+st_texture_cache_pixbuf_equal (StTextureCache *cache, GdkPixbuf *a, GdkPixbuf *b)
+{
+  size_t size_a = pixbuf_byte_size (a);
+  size_t size_b = pixbuf_byte_size (b);
+  if (size_a != size_b)
+    return FALSE;
+  return memcmp (gdk_pixbuf_get_pixels (a), gdk_pixbuf_get_pixels (b), size_a) == 0;
+}
+
+static StTextureCache *instance = NULL;
+
+/**
+ * st_texture_cache_get_default:
+ *
+ * Return value: (transfer none): The global texture cache
+ */
+StTextureCache*
+st_texture_cache_get_default (void)
+{
+  if (instance == NULL)
+    instance = g_object_new (ST_TYPE_TEXTURE_CACHE, NULL);
+  return instance;
+}
index ea299a035ef4293a7c2f2731043f20f66e7ee592..0c51a38b8a74f5bcd2e55d85e9ca2e36199eeb09 100644 (file)
 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * st-texture-cache.h: Cached textures object
- *
- * Copyright 2007 OpenedHand
- * Copyright 2009 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
-#error "Only <st/st.h> can be included directly.h"
-#endif
-
-#ifndef _ST_TEXTURE_CACHE
-#define _ST_TEXTURE_CACHE
-
-#include <glib-object.h>
-#include <clutter/clutter.h>
-
-G_BEGIN_DECLS
+#ifndef __ST_TEXTURE_CACHE_H__
+#define __ST_TEXTURE_CACHE_H__
 
-#define ST_TYPE_TEXTURE_CACHE st_texture_cache_get_type()
-
-#define ST_TEXTURE_CACHE(obj) \
-  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
-  ST_TYPE_TEXTURE_CACHE, StTextureCache))
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <clutter/clutter.h>
 
-#define ST_TEXTURE_CACHE_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_CAST ((klass), \
-  ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
+#define ST_TYPE_TEXTURE_CACHE                 (st_texture_cache_get_type ())
+#define ST_TEXTURE_CACHE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_CACHE, StTextureCache))
+#define ST_TEXTURE_CACHE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
+#define ST_IS_TEXTURE_CACHE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_CACHE))
+#define ST_IS_TEXTURE_CACHE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_CACHE))
+#define ST_TEXTURE_CACHE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
 
-#define ST_IS_TEXTURE_CACHE(obj) \
-  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
-  ST_TYPE_TEXTURE_CACHE))
+typedef struct _StTextureCache StTextureCache;
+typedef struct _StTextureCacheClass StTextureCacheClass;
 
-#define ST_IS_TEXTURE_CACHE_CLASS(klass) \
-  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
-  ST_TYPE_TEXTURE_CACHE))
+typedef struct _StTextureCachePrivate StTextureCachePrivate;
 
-#define ST_TEXTURE_CACHE_GET_CLASS(obj) \
-  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
-  ST_TYPE_TEXTURE_CACHE, StTextureCacheClass))
+struct _StTextureCache
+{
+  GObject parent;
 
-/**
- * StTextureCache:
- *
- * The contents of this structure are private and should only be accessed
- * through the public API.
- */
-typedef struct {
-    /*< private >*/
-    GObject parent;
-} StTextureCache;
+  StTextureCachePrivate *priv;
+};
 
-typedef struct {
+struct _StTextureCacheClass
+{
   GObjectClass parent_class;
 
-  void (* loaded)        (StTextureCache *self,
-                          const gchar      *path,
-                          ClutterTexture   *texture);
+};
 
-  void (* error_loading) (StTextureCache *self,
-                          GError           *error);
-} StTextureCacheClass;
+typedef enum {
+  ST_TEXTURE_CACHE_POLICY_NONE,
+  ST_TEXTURE_CACHE_POLICY_FOREVER
+} StTextureCachePolicy;
 
-GType st_texture_cache_get_type (void);
+GType st_texture_cache_get_type (void) G_GNUC_CONST;
 
 StTextureCache* st_texture_cache_get_default (void);
 
-ClutterTexture* st_texture_cache_get_texture (StTextureCache *self,
-                                              const gchar    *path);
-ClutterActor*   st_texture_cache_get_actor   (StTextureCache *self,
-                                              const gchar    *path);
-
-gint            st_texture_cache_get_size    (StTextureCache *self);
-
-void st_texture_cache_load_cache (StTextureCache *self,
-                                  const char     *filename);
-
-G_END_DECLS
-
-#endif /* _ST_TEXTURE_CACHE */
+ClutterActor *st_texture_cache_bind_pixbuf_property (StTextureCache    *cache,
+                                                     GObject           *object,
+                                                     const char        *property_name);
+
+ClutterActor *st_texture_cache_load_icon_name (StTextureCache *cache,
+                                               const char     *name,
+                                               gint            size);
+
+ClutterActor *st_texture_cache_load_gicon (StTextureCache *cache,
+                                           GIcon          *icon,
+                                           gint            size);
+
+ClutterActor *st_texture_cache_load_thumbnail (StTextureCache *cache,
+                                               int             size,
+                                               const char     *uri,
+                                               const char     *mimetype);
+
+ClutterActor *st_texture_cache_load_recent_thumbnail (StTextureCache    *cache,
+                                                      int                size,
+                                                      GtkRecentInfo     *info);
+
+void st_texture_cache_evict_thumbnail (StTextureCache *cache,
+                                       const char     *uri);
+
+void st_texture_cache_evict_recent_thumbnail (StTextureCache *cache,
+                                              GtkRecentInfo  *info);
+
+ClutterActor *st_texture_cache_load_uri_async (StTextureCache    *cache,
+                                               const gchar       *filename,
+                                               int                available_width,
+                                               int                available_height);
+
+ClutterActor *st_texture_cache_load_uri_sync (StTextureCache       *cache,
+                                              StTextureCachePolicy  policy,
+                                              const gchar          *filename,
+                                              int                   available_width,
+                                              int                   available_height,
+                                              GError              **error);
+
+ClutterActor *st_texture_cache_load_file_simple (StTextureCache *cache,
+                                                 const gchar    *file_path);
+
+ClutterActor *st_texture_cache_load_from_data (StTextureCache    *cache,
+                                               const guchar      *data,
+                                               gsize              len,
+                                               int                size,
+                                               GError           **error);
+ClutterActor *st_texture_cache_load_from_raw  (StTextureCache    *cache,
+                                               const guchar      *data,
+                                               gsize              len,
+                                               gboolean           has_alpha,
+                                               int                width,
+                                               int                height,
+                                               int                rowstride,
+                                               int                size,
+                                               GError           **error);
+
+gboolean st_texture_cache_pixbuf_equal (StTextureCache *cache, GdkPixbuf *a, GdkPixbuf *b);
+
+#endif /* __ST_TEXTURE_CACHE_H__ */
index 294fc01bb9999c59de7c71c1fe16be2af661ab3c..e5d200c1ae00098fc3fd8e4d44acde032dfaa332 100644 (file)
@@ -905,8 +905,8 @@ st_widget_real_style_changed (StWidget *self)
       /* `border-image' takes precedence over `background-image'.
        * Firefox lets the background-image shine thru when border-image has
        * alpha an channel, maybe that would be an option for the future. */
-      texture = st_texture_cache_get_texture (texture_cache,
-                                                filename);
+      texture = (ClutterTexture*) st_texture_cache_load_file_simple (texture_cache,
+                                                                     filename);
 
       clutter_texture_get_base_size (CLUTTER_TEXTURE (texture),
                                      &width, &height);
@@ -968,16 +968,8 @@ st_widget_real_style_changed (StWidget *self)
   bg_file = st_theme_node_get_background_image (theme_node);
   if (bg_file != NULL)
     {
-      texture = st_texture_cache_get_texture (texture_cache, bg_file);
-      priv->background_image = (ClutterActor*) texture;
-
-      if (priv->background_image != NULL)
-        {
-          clutter_actor_set_parent (priv->background_image,
-                                    CLUTTER_ACTOR (self));
-        }
-      else
-        g_warning ("Could not load %s", bg_file);
+      priv->background_image = st_texture_cache_load_file_simple (texture_cache, bg_file);
+      clutter_actor_set_parent (priv->background_image, CLUTTER_ACTOR (self));
 
       has_changed = TRUE;
       relayout_needed = TRUE;